Eggstravaganza

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

Missing Access control on `depositEgg` Enables Frontrunning and NFT theft risk.

Summary

The depositEgg() function in the EggVault contract is publicly callable, but it is clearly intended to be used only by the EggHuntGame contract. The game logic safely wraps the transfer and deposit in a single atomic transaction, protecting users from frontrunning.

However, by exposing depositEgg() publicly, users are unintentionally given a dangerous alternative: manually transferring their NFT and then calling depositEgg() themselves in a separate transaction. This opens the door for attackers to observe NFT transfers and frontrun the deposit call, effectively stealing the NFT and associating it with their own address in the vault.

Even though the intended and safe flow exists through the game contract, it is better practice to fully enforce this access control at the vault level, and not even allow users to call the function directly. This both prevents mistakes and protects against malicious exploitation.

Vulnerability Details

Intended safe flow (throug EggHuntGame):

nft.safeTransferFrom(msg.sender, address(vault), tokenId);
vault.depositEgg(tokenId); // called internally in same transaction

unsafe flow:

  1. user sends

nft.safeTransferFrom(msg.sender, address(vault), tokenId);
  1. plans to call :

vault.depositEgg(tokenId);
  1. Attacker frontruns and calls:

vault.depositEgg(tokenId); // Egg is now assigned to attacker

Impact

user asset theft, NFTs transferred manually to the vault can be stolen through frontrunning.

Tools Used

manual code analysis

Recommendations

Restrict depositEgg() so it can only be called by the game contract. More importantly, enforce this design intention in code, so users cannot call it directly, even by mistake:

+address public gameContract;
+modifier onlyGameContract() {
+ require(msg.sender == gameContract, "Only game contract can deposit");
+ _;
+}
-function depositEgg(uint256 tokenId) external {
+function depositEgg(uint256 tokenId) external onlyGameContract {
...
}
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.