First Flight #12: Kitty Connect

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

Pointless Access Control in `KittyBridge::_ccipReceive` Preventing Router Contract Access

[M-1] Pointless Access Control in KittyBridge::_ccipReceive Preventing Router Contract Access

Description: The _ccipReceive function within the KittyBridge contract is designed to handle the reception of cross-chain asset transfers. However, it includes an access control mechanism that restricts the function's execution to only those addresses listed in KittyBridgeBase::allowlistedSenders. This restriction is unnecessary because _ccipReceive is a internal function and is called by CCIPReceiver::ccipReceive only which is checked by CCIPReceiver::onlyRouter modifier which already checks if the msg.sender is the Router contract.
Here is the onlyAllowListed modifer which is used on _ccipReceive internal function:

modifier onlyAllowlisted(uint64 _sourceChainSelector, address _sender) {
if (!allowlistedSourceChains[_sourceChainSelector]) {
revert KittyBridge__SourceChainNotAllowlisted(_sourceChainSelector);
}
@> if (!allowlistedSenders[_sender])
@> revert KittyBridge__SenderNotAllowlisted(_sender);
_;
}

And here is the onlyRouter modifier used on ccipReceive external function, which essentially checks the same thing.

modifier onlyRouter() {
@> if (msg.sender != address(i_router)) revert InvalidRouter(msg.sender);
_;
}

Impact: This access control issue can severely impact the functionality of the NFT bridge, especially in scenarios where the CCIP Router is not included in the allowlist.(whcih already is the case because its not included in allowlist in deploy script DeployKittyConnect.s.sol ) It would prevent the bridge from receiving assets from other blockchains, effectively breaking the cross-chain functionality. This could lead to a significant loss of functionality for users and developers relying on the bridge for cross-chain NFT transfers, negatively affecting the overall user experience and the bridge's utility.

Proof of Concept: Use the following code in your test suit, Note that the message sent by Router is just random strings as it does not matter here:

function test_routerFailsToCallRecieverFunctionExtraAccessControl() public {
Client.Any2EVMMessage memory message;
// struct Any2EVMMessage {
// bytes32 messageId; // MessageId corresponding to ccipSend on source.
// uint64 sourceChainSelector; // Source chain selector.
// bytes sender; // abi.decode(sender) if coming from an EVM chain.
// bytes data; // payload sent in original message.
// EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.
// }
message = Client.Any2EVMMessage({
messageId: bytes32(abi.encode("some random message Id")),
sourceChainSelector: 14767482510784806043,
sender: abi.encode(address(kittyBridge)),
data: abi.encode("Not important data"),
destTokenAmounts: new Client.EVMTokenAmount[](0)
});
// router tries to call ccipReceive by some random message
address router = kittyBridge.getRouter();
vm.prank(router);
vm.expectRevert(
abi.encodeWithSelector(
KittyBridge__SenderNotAllowlisted.selector,
router
)
);
kittyBridge.ccipReceive(message);
}

Recommended Mitigation: To mitigate this issue, the access control mechanism in the _ccipReceive function should be reviewed and modified to ensure that it does not inadvertently restrict legitimate contracts, such as the CCIP Router, from interacting with the bridge. This could be done via two ways:

  1. Completely removing the access control check for msg.sender, as it's already done by the onlyRouter modifier, and just keeping the source chain check. This approach simplifies the function by removing unnecessary restrictions, ensuring that only the intended router can call _ccipReceive.

  2. Adding the i_router to the allowlist in the constructor, if there are specific reasons to keep the address check. This method maintains the current access control mechanism but ensures that the CCIP Router is explicitly allowed to call _ccipReceive, thus maintaining the bridge's cross-chain functionality without compromising security.
    Here's a simplified example of how the access control might be modified to allow the CCIP Router to call _ccipReceive without being on the allowlist:

modifier onlyAllowlisted(uint64 _sourceChainSelector, address _sender) {
if (!allowlistedSourceChains[_sourceChainSelector]) {
revert KittyBridge__SourceChainNotAllowlisted(_sourceChainSelector);
}
- if (!allowlistedSenders[_sender])
- revert KittyBridge__SenderNotAllowlisted(_sender);
_;
}
Updates

Lead Judging Commences

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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