First Flight #12: Kitty Connect

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

Unrecoverable funds deposited accidentally

Summary

Two custom errors within KittyBridgeBase.sol have been identified but not implemented, indicating potential issues with the recovery of user funds deposited accidentally into the contract. Additionally, the KittyConnect.sol::TokensRedeemedForVetVisit event is declared but not utilized in any functions, suggesting a missing functionality within the contract.

Vulnerability Details

Currently, there is no mechanism to recover ERC-20 or native tokens sent unintentionally by users. To address this issue, two distinct functions could be added to facilitate the recovery of each type of asset. Refer to the example below for implementation details

Impact

The absence of a recovery mechanism poses a risk of permanent loss of user funds.

Tools Used

Manual review.

Recommendations

Implement rescue functions such as the following:

struct RetrieveData {
address target;
bytes data;
uint256 value;
}
function retrieveNativeAssets(
RetrieveData[] calldata retrieveData
) external payable onlyOwner {
if (address(this).balance == 0) {
revert KittyBridge__NothingToWithdraw();
}
for (uint256 i = 0; i < retrieveData.length; ++i) {
(bool success, ) = address(retrieveData[i].target).call{
value: retrieveData[i].value
}(retrieveData[i].data);
if (!success) {
revert KittyBridge__FailedToWithdrawEth(
owner(),
retrieveData[i].target,
retrieveData[i].value
);
}
}
}
function retrieveERC20Tokens(
address token,
address to,
uint256 amount
) external onlyOwner {
IERC20(token).transfer(to, amount);
}

Find PoC associated to the rescue functions attached:

Code
function test_recoverERC20() public {
address someUser = makeAddr("someUser");
vm.startBroadcast(vm.envUint("PRIVATE_KEY"));
IERC20(networkConfig.link).transfer(someUser, 1 ether);
vm.stopBroadcast();
vm.prank(someUser);
IERC20(networkConfig.link).transfer(address(kittyBridge), 1 ether);
vm.startBroadcast(vm.envUint("PRIVATE_KEY"));
kittyBridge.retrieveERC20Tokens(networkConfig.link, someUser, 1 ether);
vm.stopBroadcast();
assertEq(IERC20(networkConfig.link).balanceOf(someUser), 1 ether);
}
function test_recoverNativeAssets() public {
address someUser = makeAddr("someUser");
vm.deal(someUser, 0.1 ether);
vm.startBroadcast(someUser);
(bool success, ) = address(kittyBridge).call{value: 0.1 ether}("");
vm.stopBroadcast();
KittyBridge.RetrieveData[]
memory retrieveData = new KittyBridge.RetrieveData[](1);
retrieveData[0] = KittyBridge.RetrieveData(someUser, "", 0.1 ether);
vm.startBroadcast(vm.envUint("PRIVATE_KEY"));
kittyBridge.retrieveNativeAssets(retrieveData);
vm.stopBroadcast();
assertEq(someUser.balance, 0.1 ether);
}
Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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