Eggstravaganza

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

Insecure Randomness Allows Predictable Egg Discovery

Summary

The searchForEgg function in EggHuntGame contract relies on an insecure and manipulable source of randomness. This allows attackers to predict or manipulate the outcome of egg discovery, giving unfair advantages and undermining the integrity of the game.

Vulnerability Details

The Vulnerability exists in generating the random number in line 71 of EggHuntGame Contract: https://github.com/CodeHawks-Contests/2025-04-eggstravaganza/blob/main/src/EggHuntGame.sol#L71

The pseudo-random value is derived using predictable inputs:

  • block.timestamp – public and validator-controlled.

  • eggCounter – public state variable.

  • msg.sender – caller-controlled.

While block.prevrandao is sourced from the block proposer in Ethereum’s PoS system and adds entropy, it’s not entirely unpredictable. In certain scenarios (especially for validators), this value can still be manipulated or anticipated with some level of confidence.

Since the game uses this value to determine if a player found an egg (random < eggFindThreshold), a malicious player can simulate calls off-chain or a validator can manipulate block inputs to meet the condition.

Impact

High Severity — This vulnerability does not just affect gameplay fairness; it can lead to loss of NFT assets meant to be uniquely discovered and earned. Malicious players or validators can:

  • Systematically and repeatedly claim rare eggs.

  • Prevent legitimate players from finding eggs.

  • Drain all available eggs unfairly, denying honest participation.

Tools Used

Manual code review and analysis of randomness entropy sources.

Proof of Concept

Scenario:

A malicious player can simulate random values off-chain and only call searchForEgg() when the outcome will succeed:

for (let i = 0; i < 100; i++) {
const fakeCounter = currentEggCounter + i;
const hash = web3.utils.soliditySha3({
t: 'uint256', v: predictedTimestamp
}, {
t: 'bytes32', v: blockPrevrandao
}, {
t: 'address', v: attackerAddress
}, {
t: 'uint256', v: fakeCounter
});
if (parseInt(hash, 16) % 100 < eggFindThreshold) {
// Now call searchForEgg from attacker wallet at predicted block timestamp
}
}

Alternatively, a validator can manipulate block.timestamp or withhold a valid block until they can craft a favorable outcome.

Recommendations

Option One: Commit-Reveal Scheme below:

Replace searchForEgg() with a secure two-phase commit-reveal scheme:

// Store hash of (secret, salt)
mapping(address => bytes32) public commitments;
/// @notice Commit phase — player submits hash of secret and salt
function commit(bytes32 commitmentHash) external {
commitments[msg.sender] = commitmentHash;
}
/// @notice Reveal phase — player reveals secret and salt
/// Contract validates the commitment and proceeds with randomness logic
function reveal(bytes32 secret, bytes32 salt) external {
bytes32 revealedHash = keccak256(abi.encodePacked(secret, salt));
require(commitments[msg.sender] == revealedHash, "Invalid commitment");
delete commitments[msg.sender]; // prevent replay
uint256 random = uint256(revealedHash) % 100;
// rest of the code
}

Option Two: Chainlink VRF (Minimal Setup):

For a trustless, on-chain verifiable solution:

  • Player initiates egg search → sends a VRF request.

  • Chainlink VRF callback fulfills randomness securely.

function fulfillRandomWords(uint256 requestId, uint256[] memory randomWords) internal override {
address player = requestToPlayer[requestId];
uint256 random = randomWords[0] % 100;
if (random < eggFindThreshold) {
eggCounter++;
eggsFound[player] += 1;
require(eggNFT.mintEgg(player, eggCounter), "Mint failed");
emit EggFound(player, eggCounter, eggsFound[player]);
}
}
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.