Mystery Box

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

Reentrancy Vulnerability in claimSingleReward() Function

Summary

The claimSingleReward() function is susceptible to reentrancy attacks, which could allow an attacker to call this function recursively via a fallback function, potentially draining funds or receiving more rewards than intended.

Vulnerability Details

The implementation does not follow the check-effects-interactions pattern. An attacker could exploit this by creating a malicious contract with a fallback function that calls claimSingleReward(), leading to a reentrancy attack and potentially draining the contract.

  • Vulnerable Code Snippet

function claimSingleReward(uint256 _index) public {
require(_index <= rewardsOwned[msg.sender].length, "Invalid index");
uint256 value = rewardsOwned[msg.sender][_index].value;
require(value > 0, "No reward to claim");
(bool success,) = payable(msg.sender).call{value: value}("");
require(success, "Transfer failed");
delete rewardsOwned[msg.sender][_index];
}

Impact

An attacker could exploit the vulnerability in the contract's reward distribution mechanism through a reentrancy attack, allowing them to repeatedly call the claimSingleReward() function before the original execution is complete. This would allow the attacker to drain the contract’s funds by claiming the same reward multiple times, or even claim rewards they are not entitled to, leading to a significant loss of funds allocated for legitimate users.

In this scenario, the attacker could manipulate the contract to withdraw more rewards than they are supposed to receive. Since the contract does not properly secure the state change (deleting the reward or marking the reward as claimed) before executing the external call (the transfer of funds), the attacker could exploit this gap. By repeatedly calling the function in a recursive manner, they could claim rewards multiple times, even if they are only entitled to claim once.

This vulnerability exposes all users interacting with the contract to a severe risk. Any user attempting to claim their rightful rewards could find that the funds have already been drained by the attacker. As a result, legitimate claimants could suffer financial losses, as there would be insufficient funds left in the contract to honor their claims. Moreover, the contract owner is also at risk of losing a significant amount of capital, potentially leading to the collapse of the entire reward system and a loss of trust in the platform.

Tools Used

manual review

Recommendations

  • Implement the check-effects-interactions pattern by moving the state changes (deleting rewards) before the external call to prevent reentrancy.

As I have shown below:

function claimSingleReward(uint256 _index) public {
//check
require(_index < rewardsOwned[msg.sender].length, "Invalid index"); // Corrected condition
uint256 value = rewardsOwned[msg.sender][_index].value;
require(value > 0, "No reward to claim");
//effect
+ delete rewardsOwned[msg.sender][_index]; // Move this line before the external call
//interaction
(bool success,) = payable(msg.sender).call{value: value}("");
require(success, "Transfer failed");
- delete rewardsOwned[msg.sender][_index];
}

  • consider using OpenZeppelin's ReentrancyGuard modifier is essential to secure the contract against reentrancy exploits and maintain the integrity of the reward distribution process.

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!