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

Lack of Request Hash Verification Allows Unauthorized Withdrawals (`bridge::withdraw_auto_from_l1`)

Summary

Vulnerability Detail

The bridge.cairo contract implements a cross-chain token bridge between L1 and L2. One of its key functions, withdraw_auto_from_l1(), is responsible for processing withdrawal requests originating from L1. This function is crucial for maintaining the integrity and security of the bridge, as it handles the transfer or minting of tokens on L2 based on the information provided in the withdrawal request.

The withdraw_auto_from_l1() function takes two parameters: from_address (the L1 sender address) and req (the withdrawal request details). It first checks if the bridge is enabled and verifies that the from_address matches the stored bridge_l1_address. However, a critical security check is missing: the function does not verify the integrity of the req data by recomputing and comparing its hash.

The Request struct contains essential information such as token IDs, owner addresses, and URIs. Without proper hash verification, an attacker could potentially alter this data after it has been signed on L1 but before it reaches L2, leading to unauthorized withdrawals or minting of tokens.

This issue stems from an incomplete implementation, as evidenced by the TODO comment in the code:

// TODO: recompute HASH to ensure data are not altered.
// TODO: Validate all fields the request (cf. FSM).

The absence of these crucial checks opens up a significant security hole in the bridge's withdrawal process.

Relevant code:

#[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');
// TODO: recompute HASH to ensure data are not altered.
// TODO: Validate all fields the request (cf. FSM).
let collection_l2 = ensure_erc721_deployment(ref self, @req);
// ... (token transfer or minting logic)
}

Impact

An attacker could alter the request data and withdraw tokens they do not own, leading to significant financial losses for users of the bridge.

Proof of Concept

  1. Alice initiates a legitimate withdrawal request on L1 to withdraw 10 tokens of ID 1234 to her address 0xAlice on L2.

  2. The L1 contract signs this request and sends it to L2.

  3. An attacker intercepts this request before it reaches the L2 withdraw_auto_from_l1() function.

  4. The attacker modifies the req data, changing the owner_l2 to their address 0xAttacker and potentially altering the token_id or quantity.

  5. The attacker submits this modified request to the withdraw_auto_from_l1() function.

  6. The function processes the request without verifying the hash, resulting in the tokens being minted or transferred to the attacker's address instead of Alice's.

Tools Used

Manual review

Recommendation

To mitigate this issue, implement hash verification for the req data in the withdraw_auto_from_l1() function. This will ensure that the request data has not been tampered with after being signed on L1.

Here's a diff of the recommended changes:

diff --git a/src/bridge.cairo b/src/bridge.cairo
index abcdef1..1234567 100644
--- a/src/bridge.cairo
+++ b/src/bridge.cairo
@@ -123,8 +123,12 @@ mod bridge {
assert(self.bridge_l1_address.read().into() == from_address,
'Invalid L1 msg sender');
- // TODO: recompute HASH to ensure data are not altered.
- // TODO: Validate all fields the request (cf. FSM).
+ // Recompute HASH to ensure data are not altered.
+ let recomputed_hash = compute_request_hash(
+ req.salt, req.collection_l2, req.owner_l1, req.ids
+ );
+ assert(recomputed_hash == req.hash, 'Request data has been altered');
+
+ // Validate all fields of the request (cf. FSM).
let collection_l2 = ensure_erc721_deployment(ref self, @req);

You guys can also implement comprehensive validation for all fields in the req struct to ensure they conform to expected values and ranges. This may include checks for valid addresses, token IDs, and other relevant data.

Updates

Lead Judging Commences

n0kto Lead Judge 12 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity
Assigned finding tags:

Informational / Gas

Please, do not suppose impacts, think about the real impact of the bug and check the CodeHawks documentation to confirm: https://docs.codehawks.com/hawks-auditors/how-to-determine-a-finding-validity A PoC always helps to understand the real impact possible.

Support

FAQs

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