Mystery Box

First Flight #25
Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: medium
Valid

Predictable Randomness in MysteryBox Reward Distribution

Summary

The MysteryBox contract uses a vulnerable method to generate random numbers for reward distribution. The randomness is based on easily predictable parameters (block.timestamp and msg.sender), making it susceptible to manipulation and allowing malicious actors to potentially influence or predict the reward selection process.

Vulnerability Details

The openBox function generates a pseudo-random number using:

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

This method is predictable because:

  1. block.timestamp can be estimated or manipulated slightly by miners.

  2. msg.sender is known to the transaction sender.

An attacker can calculate the randomValue in advance and time their transaction to increase the likelihood of receiving a desired outcome.

PoC

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import "../src/MysteryBox.sol";
contract MysteryBoxTest is Test {
MysteryBox public mysteryBox;
address public owner;
address public user;
function setUp() public {
owner = address(this);
user = address(0x1);
vm.deal(owner, 1 ether);
// Deploy the contract as the owner
vm.prank(owner);
mysteryBox = new MysteryBox{value: 0.1 ether}();
// Assume the constructor adds some initial rewards
}
function testRandomnessVulnerability() public {
// User buys a box
vm.deal(user, 1 ether);
vm.prank(user);
mysteryBox.buyBox{value: 0.1 ether}();
uint256 initialBoxCount = mysteryBox.boxesOwned(user);
// Simulate block parameters
uint256 timestamp = 1678234567;
vm.warp(timestamp);
// User opens the box
vm.prank(user);
mysteryBox.openBox();
assertLt(
mysteryBox.boxesOwned(user),
initialBoxCount,
"A box should have been opened"
);
// Demonstrate randomness vulnerability
uint256 predictedRandomValue = uint256(
keccak256(abi.encodePacked(timestamp, user))
) % 100;
console.log("Predictable random value:", predictedRandomValue);
}
}

This test demonstrates that the random value can be accurately predicted.

Impact

Users can manipulate the system to obtain desired rewards more frequently.

If exploited, users may lose confidence in the fairness of the reward system.

Tools Used

Manual code review

Recommendations

Implement Chainlink VRF (Verifiable Random Function) for secure, verifiable randomness.

Implement a two-step process using the hash of a future block for randomness.

Updates

Appeal created

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

Weak Randomness

Support

FAQs

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

Give us feedback!