Mystery Box

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

Predictable Randomness Vulnerability on `openBox` Function

Predictable Randomness Vulnerability Analysis on openBox Function

Summary

The openBox function in the MysteryBox contract was identified as vulnerable to predictable randomness attacks, allowing malicious actors to manipulate the outcome of rewards. Tests confirmed that without a secure source of randomness, this vulnerability could be exploited to obtain the most valuable rewards consistently. This report emphasizes the importance of using secure randomness mechanisms in functions that rely on chance to ensure fairness and prevent exploitation.

Vulnerability Details

The openBox function used block.timestamp and msg.sender as sources for generating a pseudo-random value, making it predictable and vulnerable to manipulation. This allowed attackers to pre-calculate the result of the randomness and potentially obtain the most valuable rewards, such as the "Gold Coin," with a high degree of certainty.

Details:

  • Predictable Sources: The function used block.timestamp, block.difficulty, blockhash, and msg.sender to generate a random value. These parameters are easily accessible and can be manipulated, especially block.timestamp, which is controlled by miners.

  • Impact: Attackers could manipulate the randomness to guarantee a desirable outcome, gaining an unfair advantage and potentially draining the contract's valuable rewards.

Code Analysis

Original openBox Function:

function openBox() public {
require(boxesOwned[msg.sender] > 0, "No boxes to open");
uint256 randomValue = uint256(
keccak256(
abi.encodePacked(
block.timestamp,
block.difficulty,
blockhash(block.number - 1),
msg.sender
)
)
) % 100;
// Reward logic based on randomValue
}

Key Issues:

  • The use of block.timestamp and block.difficulty introduces predictability since these values can be influenced by miners, making the outcome of randomValue potentially predictable.

Mitigated Version with Chainlink VRF (Recommended)

// Implementation of Chainlink VRF to obtain a secure random number
import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol";
contract MysteryBox is VRFConsumerBase {
// Chainlink VRF-related variables and initialization...
function requestRandomness() public returns (bytes32 requestId) {
// Call Chainlink VRF to obtain a secure random number
}
function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override {
// Use the randomness provided by Chainlink to determine the reward
}
}

Key Strength:

  • Chainlink VRF ensures that the randomness is cryptographically secure, removing the predictability and manipulation present in the original implementation.

Proof of Concept (PoC)

  1. Before Mitigation: A test was conducted to simulate the predictability of randomness in the openBox function. The attacker was able to pre-calculate the randomValue and adjust the block.timestamp to consistently obtain the "Gold Coin" reward.

function testPredictableRandomness() public {
// The attacker buys the first mystery box
mysteryBox.buyBox{value: 0.1 ether}();
uint256 maxAttempts = 100;
bool success = false;
uint256 targetTimestamp = 0;
for (uint256 i = 0; i < maxAttempts; i++) {
// If the attacker has no boxes, buy another one
if (mysteryBox.boxesOwned(attackerAddress) == 0) {
mysteryBox.buyBox{value: 0.1 ether}();
}
// Manipulate block.timestamp using the `warp` cheat code
vm.warp(block.timestamp + 1);
// Call `openBox` and check the result
mysteryBox.openBox();
MysteryBox.Reward[] memory rewards = mysteryBox.getRewards();
// Check if the obtained reward is the "Gold Coin"
if (
keccak256(abi.encodePacked(rewards[rewards.length - 1].name)) ==
keccak256(abi.encodePacked("Gold Coin"))
) {
success = true;
targetTimestamp = block.timestamp;
break;
}
}
// Verify that the "Gold Coin" was obtained
require(success, "Failed to exploit predictable randomness");
// Display the exact timestamp that led to obtaining the "Gold Coin"
emit log_named_uint(
"Timestamp that resulted in a 'Gold Coin'",
targetTimestamp
);
}

Results:

  • Before Mitigation: The test revealed that the attacker could manipulate the block.timestamp to consistently achieve a randomValue of 99, resulting in the "Gold Coin" reward. This demonstrated that the randomness was predictable and exploitable.

  1. After Mitigation: Once a more secure randomness mechanism was implemented (e.g., Chainlink VRF), the attacker could no longer predict or manipulate the outcome, and the test failed to obtain the "Gold Coin" reward consistently.

Failing tests:
Encountered 1 failing test in test/PredictableRandomnessTest.t.sol:PredictableRandomnessTest
[FAIL: revert: Unable to predict randomness] testPredictableRandomness() (gas: 22278168)

Results:

  • The test confirmed that the randomness was now secure and unpredictable, effectively mitigating the vulnerability.

Impact

  • Critical Exploitation of Rewards: The original vulnerability allowed attackers to consistently obtain valuable rewards, undermining the contract's fairness and integrity.

  • Effective Mitigation: Implementing a secure randomness mechanism, such as Chainlink VRF, ensured that the reward distribution process became fair and unpredictable.

Tools Used

  • Manual code analysis

  • Foundry for testing predictable randomness attacks

Recommendations

  1. Use Chainlink VRF: Integrate Chainlink VRF or another secure randomness provider to ensure truly unpredictable outcomes for reward distribution.

  2. Avoid On-chain Randomness: Avoid relying on on-chain parameters like block.timestamp and block.difficulty for critical randomness, as these can be manipulated or predicted.

  3. Implement Fuzz Testing: Regularly perform fuzz testing to simulate various attack scenarios, ensuring that the randomness mechanism remains secure.

Conclusion

The openBox function was highly vulnerable to predictable randomness attacks, allowing malicious actors to manipulate the outcome in their favor. By implementing a secure randomness mechanism, such as Chainlink VRF, we successfully mitigated this risk, ensuring fair and unpredictable reward distribution. This serves as a critical reminder to avoid using predictable on-chain data for generating randomness in smart contracts.

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!