Eggstravaganza

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

searchForEgg is vulnerable because of pseudo-random number

Summary

searchForEgg function is vulnerable as the user can create N number of new addresses and call the function in the same block and each one will have a 20% chance of getting the prize, there is no limit for this.

Vulnerability Details

searchForEgg function allows the user mint as much NFTs as he wants all at once. The attacker can set a script to use N different addresses to call that function in N different transactions in the same block or can he simply make a contract that is a factory of new contracts that will be used to call the `searchForEgg` function.

Since the msg.sender is the only thing the user can alter he can simply spam multiple of his addresses and it each one has a 20% chance of minting a new NFT.

https://github.com/CodeHawks-Contests/2025-04-eggstravaganza/blob/main/src/EggHuntGame.sol#L63-L81

/// @notice Participants call this function to search for an egg.
/// A pseudo-random number is generated and, if below the threshold, an egg is found.
function searchForEgg() external {
require(gameActive, "Game not active");
require(block.timestamp >= startTime, "Game not started yet");
require(block.timestamp <= endTime, "Game ended");
// Pseudo-random number generation (for demonstration purposes only)
uint256 random = uint256(
keccak256(abi.encodePacked(block.timestamp, block.prevrandao, msg.sender, eggCounter))
) % 100;
if (random < eggFindThreshold) {
eggCounter++;
eggsFound[msg.sender] += 1;
eggNFT.mintEgg(msg.sender, eggCounter);
emit EggFound(msg.sender, eggCounter, eggsFound[msg.sender]);
}
}

foundry test: [PASS] testMint() (gas: 256705)
Logs:
3

3 NFTs minted all in one call, but can be set to as many addresses the attacker wants.

function testMint() external {
vm.prank(0x00000000000000000000000000000000000021a9);
try game.searchForEgg() {} catch {}
vm.prank(0x00000000000000000000000000000000000021a9);
try game.searchForEgg() {} catch {}
vm.prank(0x0000000000000000000000000000000000000722);
try game.searchForEgg() {} catch {}
uint256 eggsAfter = game.eggCounter();
console.log(eggsAfter);
vm.assertGt(eggsAfter, 2);
}

Impact

pseudo-numbers are not safe and should never be used in production as they are predictable.

Tools Used

Foundry

Recommendations

use RNG from Chainlink instead to secure true random numbers.

Updates

Lead Judging Commences

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

Insecure Randomness

Insecure methods to generate pseudo-random numbers

Support

FAQs

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