First Flight #12: Kitty Connect

First Flight #12: Kitty Connect
Beginner FriendlyFoundryNFTGameFi
100 EXP
View results
Submission Details
Severity: high
Valid

Lack of access control in `KittyBridge:_ccipReceive()` allows anyone to mint a bridged cat without having to own it on the other chain

Severity

Impact: Medium, since the owner can still bridge his NFT

Likelihood: High, since it can be performed by almost anyone

Description

_ccipReceive() is a critical function in the CCIP system which is invoked on the receiver contract by the CCIP router.

The issue is found in the passing of msg.sender as the _sender parameter of the onlyAllowlisted modifier from KittyBridgeBase.

However, in order for the function not to revert, msg.sender can only be the address of the CCIP router because of the following public function exposed by CCIPReceiver (that KittyBridge inherits) that has the onlyRouter modifier:

/// @inheritdoc IAny2EVMMessageReceiver
function ccipReceive(Client.Any2EVMMessage calldata message) external virtual override onlyRouter {
_ccipReceive(message);
}

This makes possible for anyone to mint a cat NFT by directly calling the router without passing from KittyConnect:bridgeNftToAnotherChain(), the only function that is responsible for checking that the caller actually owns the NFT.

POC

Refer to the following diagram: https://drive.google.com/file/d/1JolPeIiFjYYS1Um8TkUWhMSTg0oa2IWj/view?usp=sharing

Recommendations

To always make sure that the NFT is owned and burn by the catOwner on the source chain, we must add proper access control to the functions listed below:

  1. protect KittyBridge:bridgeNftWithData() to only allow the KittyConnect contract to call it

  2. Change the second parameter of the onlyAllowListed as follows to pass the actual sender of the message (which will be the KittyBridge on the source chain)

onlyAllowlisted(
any2EvmMessage.sourceChainSelector,
+ abi.decode(any2EvmMessage.sender, (address))
)

Of course, the KittyBridge on the destination chain, must allow-list the address of the KittyBridge on the source chain in his allowlistedSenders mapping.

The any2EvmMessage.sender is safely set by the CCIP router on the source chain:

function ccipSend(uint64 destinationChainSelector, EVM2AnyMessage message)
{
//..
IEVM2AnyOnRamp(onRamp).forwardFromRouter(destinationChainSelector, message, feeTokenAmount, msg.sender);
}

Also refer to the following ChainLink CCIP official example:
https://docs.chain.link/ccip/tutorials/programmable-token-transfers-defensive#tutorial

Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

`onlyAllowlisted` modifier is not properly implemented in case of _ccipReceive

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.