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

Reentrancy Risk Due to Missing nonReentrant Modifier and Incorrect Check-Effects-Interactions in _withdrawFromEscrow

Summary

The safeTransferFrom functions in OpenZeppelin's ERC721 and ERC1155 trigger external calls to user addresses, which may expose the contract to reentrancy attacks. The current implementation of _withdrawFromEscrow does not include a nonReentrant modifier and fails to adhere to the check-effects-interactions pattern, as _escrow[collection][id] is cleared only after the external token transfer is executed.

Vulnerability Details

https://github.com/Cyfrin/2024-07-ark-project/blob/273b7b94986d3914d5ee737c99a59ec8728b1517/apps/blockchain/ethereum/src/Bridge.sol#L201

function withdrawTokens(
uint256[] calldata request
)
external
payable
returns (address)
{
...
bool wasEscrowed = _withdrawFromEscrow(ctype, collectionL1, req.ownerL1, id);
...}

https://github.com/Cyfrin/2024-07-ark-project/blob/main/apps/blockchain/ethereum/src/Escrow.sol#L79
the withdrawTokens() do a call to _withdrawFromEscrow() to transfer tokens

function _withdrawFromEscrow(
CollectionType collectionType,
address collection,
address to,
uint256 id
)
internal
returns (bool)
{
...
if (collectionType == CollectionType.ERC721) {
IERC721(collection).safeTransferFrom(from, to, id);
} else {
IERC1155(collection).safeTransferFrom(from, to, id, 1, "");
}
_escrow[collection][id] = address(0x0);
..
}

In _withdrawFromEscrow() the _escrow[collection][id] variable is cleared after the external token transfer via safeTransferFrom. This breaks the check-effects-interactions pattern and leaves the contract vulnerable to reentrancy attacks during the external call.

Attack Path

User Escrows Token: The user escrows an ERC721 or ERC1155 token in the contract.

Malicious Contract as Receiver: The attacker creates a malicious contract that implements onERC721Received or onERC1155Received, containing reentrant code that calls back into the vulnerable contract.

Token Withdrawal Initiated: The attacker calls the withdrawTokens function to withdraw the token from the escrow.

Reentrant Call Triggered: During the call to safeTransferFrom, the malicious contract's onERC721Received or onERC1155Received function is triggered, allowing the reentrant call to execute before _escrow[collection][id] is cleared.

Exploit Reentrancy: The malicious contract repeatedly calls withdrawTokens while the original call is still executing, potentially withdrawing multiple tokens or manipulating the contract's state.

Impact

This reentrancy can allow the attacker to withdraw multiple tokens in a single transaction. By specifying different token IDs in each reentrant call, the attacker can continue draining the escrow until the contract updates its internal state to mark the tokens as withdrawn.

Tools Used

Manual review

Recommendations

I recommend both of these solutions though either one will be sufficient on its own:

Add nonReentrant modifier
Set _escrow[collection][id] = address(0x0) before performing IERC1155/IERC721(collection).safeTransferFrom() to apply the checks-effects-interactions pattern.

Updates

Lead Judging Commences

n0kto Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality
Assigned finding tags:

finding-withdraw-reentrancy-creates-unbridgeable-tokens

Impact: - NFT already bridged won’t be bridgeable anymore without being stuck. Likelyhood: Low. - Attackers will corrupt their own tokens, deploying a risky contract interacting with an upgradable proxy. They have to buy and sell them without real benefits, except being mean. Some really specific and rare scenario can also trigger that bug.

Support

FAQs

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