First Flight #12: Kitty Connect

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

`KittyBridge::bridgeNftWithData` lacks approval for LINK token spending causing the function to always revert

Summary

KittyBridge::bridgeNftWithData does not approve router for spending LINK tokens on behalf of the KittyBridge contract before calling ccip::Router::ccipSend. This results in KittyBridge::bridgeNftWithData always reverting.

Vulnerability Details

ccip::Router::ccipSend internally calls SafeERC20::safeTransferFrom when the fee token used to pay for the transaction fee is LINK. Therefore, the caller of this function (KittyBridge) needs to approve the ccip::Router contract for spending LINK tokens before calling ccip::Router::ccipSend. Failing to do so will result in the transaction being reverted due to a lack of allowance.

function bridgeNftWithData(uint64 _destinationChainSelector, address _receiver, bytes memory _data)
external
onlyAllowlistedDestinationChain(_destinationChainSelector)
validateReceiver(_receiver)
returns (bytes32 messageId)
{
// Create an EVM2AnyMessage struct in memory with necessary information for sending a cross-chain message
Client.EVM2AnyMessage memory evm2AnyMessage = _buildCCIPMessage(_receiver, _data, address(s_linkToken));
// Initialize a router client instance to interact with cross-chain router
IRouterClient router = IRouterClient(this.getRouter());
// Get the fee required to send the CCIP message
uint256 fees = router.getFee(_destinationChainSelector, evm2AnyMessage);
if (fees > s_linkToken.balanceOf(address(this))) {
revert KittyBridge__NotEnoughBalance(s_linkToken.balanceOf(address(this)), fees);
}
// @audit The following line will revert due to a lack of allowance
messageId = router.ccipSend(_destinationChainSelector, evm2AnyMessage);
emit MessageSent(messageId, _destinationChainSelector, _receiver, _data, address(s_linkToken), fees);
return messageId;
}

Impact

Calls to the KittyBridge::bridgeNftWithData function will always revert, preventing any user from bridging their NFT.

Tools Used

Manual review.

Recommended Mitigation

Approve the ccip::Router for spending tokens on behalf of the KittyBridge before calling ccip::Router::ccipSend.

if (fees > s_linkToken.balanceOf(address(this))) {
revert KittyBridge__NotEnoughBalance(s_linkToken.balanceOf(address(this)), fees);
}
+ // approve the Router to transfer LINK tokens on contract's behalf. It will spend the fees in LINK
+ s_linkToken.approve(address(router), fees);
messageId = router.ccipSend(_destinationChainSelector, evm2AnyMessage);
Updates

Lead Judging Commences

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

Missing fee token approval

Support

FAQs

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