KittyBridge::_ccipReceive
has the purpose of gatekeeping cross-chain messages by validating the sender and the chain id of the source chain. The problem in this process is who validates the function as the sender, instead of validating the originator the message, the function validates the caller of KittyBridge::ccipReceive
which is the router of the destination chain - this means that as long the message comes from a valid source chain and the msg.sender
is the router, that message will be accepted. This will allow anyone to create a message to instruct the bridge to mint a kitty for free.
The flow of the exploit can be explained in the following steps:
Attacker deploys a contract with the ability to send cross-chain messages on an allowlisted source chain
Attacker sends a transaction to instructo the contract to send a cross-chain message to a bridge on a destination chain
The bridge on the destination chain receives the message and it is accepted because the msg.sender
is the router and the source chain is allowedlisted
The bridge calls KittyConnect::mintBridgedNFT
with the data of the cross-chain message that contains the metadata of the NFT to mint to the specified owner
To prove the attack I've deployed the protocol on Polygon Mumbai and Ethereum Sepolia. Also I've deployed an Attacker contract at this address on Polygon Mumbai for faster testing. The attacker contract is already funded with LINK to pay for the fees.
Follow the next steps:
Call KittyBridgeAttacker::computePayload
on MUMBAI_ATTACKER
with the address of the account to be owner of the kitty to mint on the destination chain
Call KittyBridgeAttacker::sendMessagePayLINK
with the chain id of Ethereum Sepolia, the receiver of the message (SEPOLIA_KITTY_BRIDGE
) and the computed payload. The chain id of sepolia and address of the receiver are 16015286601757825753
and 0xFd829a345fd28a0f6eeD6851822ebbCe6b923702
, respectively
Use the transaction hash to find the transaction on CCIP Explorer and wait for finality
After reaching block finality, verify the specified owner got a free NFT by callingKittyConnect::ownerOf
, I recommend testing from three onward as token id.
Anyone can get free kitties for free, breaking the intended behaviour of the protocol.
Manual review
To properly gatekeep messages I recommend validating the originator of the message instead of the msg.sender
.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.