Mystery Box

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

Reentrancy Vulnerability in claimAllRewards() and claimSingleReward() Functions

Summary

The claimAllRewards() and claimSingleReward(uint256) functions in the MysteryBox contract are vulnerable to a reentrancy attack. Both functions modify state variables after making external calls to msg.sender. This allows an attacker to repeatedly invoke the functions before the state is updated, enabling the theft of funds through multiple reward claims.

Vulnerability Details

The affected functions involve:

(success, ) = address(msg.sender).call{value: totalValue}(); // External call
delete rewardsOwned[msg.sender]; // State change after external call

Key Issue:

  • The external call to msg.sender.call occurs before the state variable rewardsOwned[msg.sender] is updated.

  • This allows an attacker to re-enter the function before the state is cleared, potentially allowing them to claim rewards multiple times before the state is properly updated.

Example of an Exploit

Reentrancy Attack Scenario:

Initial Setup: An attacker has a malicious contract and triggers the claimAllRewards() or claimSingleReward() function.

Execution:

  • The attacker’s contract re-enters the claimAllRewards() or claimSingleReward() functions before the state variable rewardsOwned[msg.sender] is updated, allowing them to call the function multiple times and drain the contract's funds.

Outcome: The attacker successfully claims rewards multiple times before the state is cleared, leading to a loss of funds for the contract.

Impact

  • Potential loss of all funds in the contract: If exploited, the vulnerability could allow an attacker to continuously claim rewards, potentially emptying the contract's balance.

Tools Used

Manual Review

Recommendations

To mitigate the reentrancy vulnerability, apply the Checks-Effects-Interactions pattern and use Reentrancy Guards.

Apply Checks-Effects-Interactions Pattern:

  • Update the state (clear rewardsOwned) before performing the external call.

Example fix for claimAllRewards():

// First update state
delete rewardsOwned[msg.sender];
// Then, make the external call
(success, ) = address(msg.sender).call{value: totalValue}();

Use Reentrancy Guards:

Implement a reentrancy guard using ReentrancyGuard.sol from OpenZeppelin to prevent functions from being called multiple times within the same transaction.

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";
contract MysteryBox is ReentrancyGuard {
...
function claimAllRewards() public nonReentrant {
...
}
}

This will ensure that no re-entrant calls can occur during the execution of the reward claiming functions.

Updates

Appeal created

inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Validated
Assigned finding tags:

`claimAllRewards` reentrancy

`claimSingleReward` reentrancy

Support

FAQs

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