An attacker can always win the EggHuntGame by precalculating the random value required to fulfill the winning condition. This is possible because the msg.sender
parameter in the random number calculation is user-controlled, while all other inputs are either known, predictable, or constant at the time of computation.
This vulnerability provides an unfair advantage to attackers and drastically inflates the NFT supply, thereby undermining the rarity and value of the tokens.
During the EggHuntGame, players can attempt to find an "egg"—an EggstravaganzaNFT
token—by calling EggHuntGame::searchForEgg()
. This function computes a "random" number, and if it is below a predefined eggFindThreshold
, an NFT is minted to the player via the EggstravaganzaNFT
contract.
The vulnerability lies in how the "random" number is generated, specifically in line 72 of the EggHuntGame.sol contract. The randomness is derived from inputs that are either:
predictable (block.timestamp
, block.prevrandao
)
constant or accessible (game.eggCounter()
)
attacker-controlled (msg.sender
)
By using a contract with a predetermined address (via the CREATE2
opcode and a precalculated salt), an attacker can ensure the "random" number is always below the eggFindThreshold
, effectively guaranteeing an egg is found on each attempt.
The attacker deploys a contract (EggSearcher
) with a carefully calculated address such that it always satisfies the winning condition. This contract then calls searchForEgg()
and automatically transfers the minted egg to the attacker.
The following Foundry test demonstrates the exploit. Save this in the test/PoC.t.sol
directory of the codebase:
To run the test, execute the following commands:
The expected output should be the following:
This vulnerability completely breaks the core mechanic of the EggHuntGame. Instead of relying on chance and fairness, a malicious player can mint eggs deterministically, irrespective of the set difficulty (eggFindThreshold
). This undermines the game's integrity and devalues the NFTs by flooding the supply.
Replace the current pseudo-random number generation with a secure and verifiable source of randomness, such as Chainlink VRF. The EggHuntGame::searchForEgg()
function should be refactored to request a random number using requestRandomWords()
and act upon it within a secure fulfillRandomWords()
callback. This would eliminate any user control over the randomness and prevent exploit scenarios like this.
Insecure methods to generate pseudo-random numbers
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.