NFTBridge
60,000 USDC
View results
Submission Details
Severity: low
Invalid

Inconsistent Check in Bridge Contract

Summary

The Bridge contract in the Ark Lane protocol contains a critical security inconsistency in its cancelRequest function. While other functions in the contract, such as depositTokens, check if the bridge is enabled before executing, the cancelRequest function does not perform this check.

The relevant code can be found here:

function cancelRequest(
uint256[] memory payload,
uint256 nonce
) external {
IStarknetMessaging(_starknetCoreAddress).cancelL1ToL2Message(
snaddress.unwrap(_starklaneL2Address),
felt252.unwrap(_starklaneL2Selector),
payload,
nonce
);
Request memory req = Protocol.requestDeserialize(payload, 0);
_cancelRequest(req);
emit CancelRequestCompleted(req.hash, block.timestamp);
}

This inconsistency creates a potential security loophole where users can bypass the intended security measures by using the cancelRequest function even when the bridge is disabled.

Impact

The absence of the isEnabled check in the cancelRequest function could lead to:
Unauthorized token transfers when the bridge is intentionally disabled due to security concerns. This is particularly critical for the Ark Lane protocol, as it deals with cross-chain NFT transfers between Ethereum L1 and Starknet L2.

POC

Use the following testcase to reproduce the behavior where we have disabled the bridge but still able to transfer NFT.

function test_cancelRequest123() public {
uint256[] memory ids = new uint256[](2);
ids[0] = 0;
ids[1] = 9;
(uint256 nonce, uint256[] memory payload) = setupCancelRequest(
alice,
ids
);
assert(IERC721(erc721C1).ownerOf(ids[0]) != alice);
assert(IERC721(erc721C1).ownerOf(ids[0]) != alice);
Request memory req = Protocol.requestDeserialize(payload, 0);
vm.expectEmit(true, false, false, false, bridge);
emit CancelRequestStarted(req.hash, 42);
IStarklane(bridge).startRequestCancellation(payload, nonce);
// disable bridge
IStarklane(bridge).enableBridge(false);
vm.expectEmit(true, false, false, false, bridge);
emit CancelRequestCompleted(req.hash, 42);
vm.startPrank(bob);
IStarklane(bridge).cancelRequest(payload, nonce);
vm.stopPrank();
assert(IERC721(erc721C1).ownerOf(ids[0]) == alice);
assert(IERC721(erc721C1).ownerOf(ids[1]) == alice);
}
Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Design choice
Assigned finding tags:

invalid-cancel-when-bridge-disable

Technically, if you cancel a message, the token is not really bridged. If you can withdraw, it means that the token has already been bridged. Those two funtions do not have to be disable when the bridge is. Moreover nothing should prevent users to get back their NFT.

Support

FAQs

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