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

Unable to remove whitelist collection because of incorrect Linked List element removing logic

Summary

Because of the incorrect implementation of removing an element from a Linked List, removing whitelisted NFTs will not be possible.

Vulnerability Details

This issue is related to Linked List Data Structures.

The current Data Structure used for Storing WhiteListed NFTs on Starknet is Linked List. In Ethereum, we are using arrays, but the Protocol preferred to use LinkedList in Starknet.

When removing an element from a linked list, we need to start from the Head then go to the Next, then Next ... till we reach the element. But in the current removing logic, the protocol forgot to Move to the Next element when removing.

bridge.cairo#L523-L537

// removed element from linked list
loop {
let (active, next) = self.white_listed_list.read(prev);
if next.is_zero() {
// end of list
break;
}
if !active {
break;
}
if next == collection {
let (_, target) = self.white_listed_list.read(collection);
self.white_listed_list.write(prev, (active, target));
break;
}
// @audit where is `prev = next` step
};

As we can see from the loop we are just reading the prev element, which is the head when beginning iteration, check for the next element if it is zero, or not activated. and check for the next element to be the collection we need to remove, But when we finish the iteration, we are not moving a step forward i.e prev = next, so this will make us loop in an infinite loop, leading to the tx reverted in the last because of the consumption of all gas.

This is not the case when adding new element, where the team handled it correctly.

bridge.cairo#L512

// find last element
loop {
...
@> prev = next;
};

This will make removing any element in the list except first one in most cases unable to get removed.

Proof of Concept

We made a simple Script to demonstrate how removing an element will not succeed, and the tx will revert when doing this.

Add this test in apps/blockchain/starknet/src/tests/bridge_t.cairo.

#[test]
fn auditor_whitelist_collection_remove_collection_dos_issue() {
let erc721b_contract_class = declare("erc721_bridgeable");
let BRIDGE_ADMIN = starknet::contract_address_const::<'starklane'>();
let BRIDGE_L1 = EthAddress { address: 'starklane_l1' };
let bridge_address = deploy_starklane(BRIDGE_ADMIN, BRIDGE_L1, erc721b_contract_class.class_hash);
let bridge = IStarklaneDispatcher { contract_address: bridge_address };
start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN);
bridge.enable_white_list(true);
stop_prank(CheatTarget::One(bridge_address));
let collection1 = starknet::contract_address_const::<'collection1'>();
let collection2 = starknet::contract_address_const::<'collection2'>();
let collection3 = starknet::contract_address_const::<'collection3'>();
let collection4 = starknet::contract_address_const::<'collection4'>();
let collection5 = starknet::contract_address_const::<'collection5'>();
start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN);
bridge.white_list_collection(collection1, true);
bridge.white_list_collection(collection2, true);
bridge.white_list_collection(collection3, true);
bridge.white_list_collection(collection4, true);
bridge.white_list_collection(collection5, true);
stop_prank(CheatTarget::One(bridge_address));
// Check that Collections has been added successfully
let white_listed = bridge.get_white_listed_collections();
assert_eq!(white_listed.len(), 5, "White list shall contain 5 elements");
assert_eq!(*white_listed.at(0), collection1, "Wrong collection address in white list");
assert_eq!(*white_listed.at(1), collection2, "Wrong collection address in white list");
assert_eq!(*white_listed.at(2), collection3, "Wrong collection address in white list");
assert_eq!(*white_listed.at(3), collection4, "Wrong collection address in white list");
assert_eq!(*white_listed.at(4), collection5, "Wrong collection address in white list");
assert!(bridge.is_white_listed(collection1), "Collection1 should be whitelisted");
assert!(bridge.is_white_listed(collection2), "Collection2 should be whitelisted");
assert!(bridge.is_white_listed(collection3), "Collection3 should be whitelisted");
assert!(bridge.is_white_listed(collection4), "Collection4 should be whitelisted");
assert!(bridge.is_white_listed(collection5), "Collection5 should be whitelisted");
// This should Revert
start_prank(CheatTarget::One(bridge_address), BRIDGE_ADMIN);
bridge.white_list_collection(collection3, false);
stop_prank(CheatTarget::One(bridge_address));
}

To run it you can write this command on apps/blockchain/starknet path

snforge test auditor_whitelist_collection_remove_collection_dos_issue

Output:

[FAIL] starklane::tests::bridge_t::tests::auditor_whitelist_collection_remove_collection_dos_issue
Failure data:
Got an exception while executing a hint: Hint Error: Error in the called contract (0x03b24bdfb3983f3361a7f81e871041cc45f3e1c21bfe3f1cbcaf7bec224627d5):
Error at pc=0:7454:
Could not reach the end of the program. RunResources has no remaining steps.
Cairo traceback (most recent call last):
...

Impact

  • Inability to remove whitelisted collections.

  • If a specific whitelisted NFT collection is malicious, we cannot unwhitelist it.

  • The only way to remove a specific NFT will be to start removing from the head till we reach that NFT collection to be unwhitelisted which will affect other NFT collections and is not an applicable solution in production.

Tools Used

Manual Review + Starknet Foundry

Recommendations

Move to the next element in the linked list once completing the iteration.

diff --git a/apps/blockchain/starknet/src/bridge.cairo b/apps/blockchain/starknet/src/bridge.cairo
index 23cbf8a..35e003b 100644
--- a/apps/blockchain/starknet/src/bridge.cairo
+++ b/apps/blockchain/starknet/src/bridge.cairo
@@ -535,6 +535,7 @@ mod bridge {
self.white_listed_list.write(prev, (active, target));
break;
}
+ prev = next;
};
self.white_listed_list.write(collection, (false, no_value));
}
Updates

Lead Judging Commences

n0kto Lead Judge 9 months ago
Submission Judgement Published
Validated
Assigned finding tags:

finding-L2-unwhitelist-from-third collection-impossible

Likelyhood: High, owner can only unwhitelist the 2 first collections. Impact: Medium/High, owner has to empty the list to remove any collection, and replace all the new ones.

Support

FAQs

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