Eggstravaganza

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

Predictable Puzzle Solution in Egg Hunt Game

Summary

The puzzle verification mechanism in the egg hunt game uses predictable on-chain data (block.timestamp and msg.sender), allowing attackers to bypass the puzzle requirement and mint unlimited Egg NFTs.

Vulnerability Details

Vulnerable Code

solidity

// EggHuntGame.sol
function _verifyPuzzle(bytes32 solution) internal view returns (bool) {
return keccak256(abi.encodePacked(msg.sender, block.timestamp)) == solution;
}

Flaw Analysis

  • Attack Vector:

    • block.timestamp is public and predictable (varies by ±1-15 seconds depending on chain activity)

    • Attackers can precompute valid solutions by iterating through possible timestamps

  • Exploit Scenario:

    1. Attacker calculates keccak256(abi.encodePacked(attackerAddress, futureTimestamp))

    2. Submits solution when block.timestamp matches prediction

    3. Mints unlimited eggs without solving actual puzzles

Impact

  • Unlimited NFT minting → Inflation of egg supply → Collapse of in-game economy

  • Loss of funds (if minting requires payment)

Tools Used

Manual review.

Proof of concept

// Sample Attack Script (JavaScript + ethers.js)
const attackerAddress = "0x...";
const gameContract = new ethers.Contract(...);
async function exploit() {
// Predict timestamp for next block
const futureTimestamp = Math.floor(Date.now() / 1000) + 15;
// Generate solution
const solution = ethers.utils.keccak256(
ethers.utils.defaultAbiCoder.encode(
["address", "uint256"],
[attackerAddress, futureTimestamp]
)
);
// Wait until predicted timestamp
await network.provider.send("evm_setNextBlockTimestamp", [futureTimestamp]);
// Execute attack
await gameContract.searchEgg(solution, { value: MINT_FEE });
}

Recommendations

Off-Chain Verification with Oracle
Use Chainlink Oracle to verify puzzle solutions off-chain:

// Updated Verification Flow
function searchEgg(bytes32 puzzleId) external payable {
require(gameActive, "Game inactive");
bytes32 requestId = requestRandomness(ORACLE_KEY_HASH);
_pendingRequests[requestId] = msg.sender;
}
// Oracle callback
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
address player = _pendingRequests[requestId];
_mintEgg(player, randomness);
}
Updates

Lead Judging Commences

m3dython Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality
Assigned finding tags:

Insecure Randomness

Insecure methods to generate pseudo-random numbers

Appeal created

calvinminyate Submitter
8 months ago
m3dython Lead Judge
8 months ago
m3dython Lead Judge 8 months ago
Submission Judgement Published
Invalidated
Reason: Lack of quality

Support

FAQs

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

Give us feedback!