Eggstravaganza

First Flight #37
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Valid

Missing Authorization in the depositEgg()

Summary

The depositEgg() function in the EggVault.sol contract is publically accessible and allow any caller to specify a depositor address. This opens the contratc to a critical vulnerability where a malicious actor can hijack the depositor mapping and later withdraw the NFTs they do not own.

Vulnerability Details

The function signature:

function depositEgg(uint256 tokenId, address depositor) public

allows any caller to call the depositEgg() function with an arbitrary depositor address. The only validation performed is;

require(eggNFT.ownerOf(tokenId) == address(this), "NFT not transferred to vault");

the above condition mererly verifies the NFT has been transferred to the EggVault contract but does not ensure that the depositor is the righgul owner or who transferred the NFT.

Attack Scenario:

  • A user transfers the NFT to the Vault contract.

  • Before they call the depositEgg(), an attacker frontruns the transaction and calls-

    depositEgg(uint256 tokenId, address attackerAddress)

  • Now the attacker has registered as the depositor.

  • Later, attacker can call the withdrawEgg(uint256 tokenId) function and takes the ownership of the NFT

Impact

  • High Severity

  • Complete loss of a token/NFT

  • Game integrity

  • The vulnerability is not mitigated by existing test cases

Tools

  • Manual code review

  • Logical simulation of race conditions and fron-running scenarios

Recommendations

  • Redesign the depositEgg() function to enforce authorization:

  • Remove the depositor paramater entirely

  • Use msg.sender as the sole source of truth who is depositing the NFT

  • Perform the NFT transfer within the depositEgg() function itself to enforce atomicity

function depositEgg(uint256 tokenId) external {
// Require approval has been granted to this contract for the transfer
// (handled by ERC721's transferFrom logic)
eggNFT.transferFrom(msg.sender, address(this), tokenId);
require(!storedEggs[tokenId], "Egg already deposited");
storedEggs[tokenId] = true;
eggDepositors[tokenId] = msg.sender;
emit EggDeposited(msg.sender, tokenId);
}
Updates

Lead Judging Commences

m3dython Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Frontrunning Vulnerability DepositEgg

Front-running depositEgg allows deposit ownership hijacking.

Support

FAQs

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