The depositEgg
function takes the depositor
address as a parameter. Crucially, it checks if the NFT tokenId
is already held by the vault contract (eggNFT.ownerOf(tokenId) == address(this)
) but does not verify that the msg.sender
calling the function has any relation to the depositor
address being provided or the NFT being deposited.
The attack scenario is as follows: A legitimate user finds an egg (NFT) and decides to deposit it. They first approve the EggVault
contract and then transfer the NFT to the EggVault
's address, intending to call depositEgg
afterwards. Then an attacker monitoring the blockchain sees the NFT transfer to the EggVault
address.
Before the legitimate user (or the EggHuntGame
contract) calls depositEgg
, the attacker calls depositEgg(tokenId, attackerAddress)
. Since the NFT is already in the vault, the first require
passes. If the egg wasn't previously recorded as deposited, the second require
also passes.
The vault now incorrectly records the attackerAddress
as the eggDepositors[tokenId]
.
Finally the attacker can now call withdrawEgg(tokenId)
. The check eggDepositors[tokenId] == msg.sender
will pass because the attacker's address was recorded, allowing them to steal the NFT.
Direct theft of user NFTs deposited into the vault
Make depositEgg
callable only by EggHuntGame
. Also modify depositEgg
to require msg.sender
to be the depositor or have prior authorization.
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.