The depositEgg(uint256 tokenId, address depositor) function in the EggVault contract allows arbitrary specification of the depositor address. This design enables front-running attacks, where an attacker can hijack the legitimate depositor’s NFT and register themselves as the owner in the vault. As a result, they can withdraw NFTs they do not own.
The function currently allows any caller to assign the eggDepositors[tokenId] value by passing in any address:
If a user sends their NFT to the vault contract via transferFrom(), there is a time window before they can call depositEgg(). During this time, a malicious actor can observe the transaction in the mempool and quickly call depositEgg() with the victim’s tokenId, setting themselves as the depositor.
Later, they can call withdrawEgg(tokenId) and receive the victim’s NFT — with full authorization from the vault contract.
In this PoC, the attacker successfully:
• Intercepts the NFT transfer,
• Calls depositEgg() first with their own address,
• Withdraws an NFT they never owned.
Expected Output:
Running 1 test for test/EggVault.t.sol:EggVaultTest
[PASS] testFrontRunningDepositorSpoofing() (gas: XXXX)
Impact:
• Attackers can steal NFTs from other users by front-running depositEgg() calls.
• The contract state will reflect incorrect depositor ownership.
• This breaks the trust and intended behavior of the NFT deposit system and results in loss of user assets.
• Manual code review
1. Remove the depositor argument completely and use msg.sender to record the depositor:
2. Consider replacing the two-step transfer/deposit with a single atomic flow, like safeTransferFrom + onERC721Received.
3. Add optional reentrancy protection using ReentrancyGuard to future-proof against related issues in withdrawEgg().
Front-running depositEgg allows deposit ownership hijacking.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.