NFT Dealers

First Flight #58
Beginner FriendlyFoundry
100 EXP
Submission Details
Impact: high
Likelihood: high

Reentrancy via ERC721 Receiver Enables Double-Execution of Buy Logic

Author Revealed upon completion

Root + Impact

Description

  • Normally, buying an NFT should be an atomic operation where payment, fee accounting, and NFT transfer complete exactly once.

The contract transfers the NFT to the buyer before fully finalizing internal state, allowing a malicious buyer contract implementing onERC721Received to reenter and execute marketplace logic again.

// @> External call before critical state finalization
IERC721(nft).safeTransferFrom(
listing.seller,
buyer,
tokenId
);

Risk

Likelihood:

  • Occurs when the buyer is a contract implementing onERC721Received.

Happens during normal buy() execution.

Impact:

  • Marketplace state can be reentered and manipulated.

Fees, listings, or collateral can be double-processed, enabling fund loss.

Proof of Concept

  • Attacker deploys malicious buyer contract

  • Calls buy(tokenId)

  • NFT transfer triggers onERC721Received

  • Reentrant buy() executes with partially updated state

contract Attacker is IERC721Receiver {
function onERC721Received(...) external returns (bytes4) {
// Reenter marketplace during buy
dealers.buy(tokenId);
return IERC721Receiver.onERC721Received.selector;
}
}

Recommended Mitigation

  • Apply Checks-Effects-Interactions

Add reentrancy guard

- remove this code
+ add this code
// Before external NFT transfer
delete s_listings[tokenId];
// Or
nonReentrant modifier on buy()

Support

FAQs

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

Give us feedback!