The MysteryBox::claimSingleReward
function is intended to allow users to claim a reward associated with their account. However, this function is vulnerable to a reentrancy attack due to the improper sequence of operations in the function body.
The external call to payable(msg.sender).call{value: value}("");
occurs before the state variable rewardsOwned[msg.sender][_index]
is updated (via delete). This allows an attacker to re-enter the function before the state change is made, potentially claiming rewards multiple times before the state is updated, resulting in unexpected behavior or loss of funds.
If exploited, an attacker could repeatedly call the claimSingleReward
function by re-entering it through the call to msg.sender. This would allow the attacker to drain all rewards for a specific account or even other users’ rewards.
Consider the following scenario:
A malicious actor calls claimSingleReward
.
Before the delete rewardsOwned[msg.sender][_index]
statement is executed, the actor is able to re-enter the function because the state is not yet updated.
The actor calls the function again during the reentrancy, exploiting the fact that the rewards for that index have not been deleted yet.
The actor repeats this process, draining all the rewards.
Manual Review
To prevent this reentrancy issue, the function should follow the Check-Effects-Interaction (CEI) pattern by updating the state before making the external call.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.