Puppy Raffle

AI First Flight #1
Beginner FriendlyFoundrySolidityNFT
EXP
View results
Submission Details
Impact: high
Likelihood: high
Invalid

Strict equality donation attack prevents withdrawing accumulated fees

Description

  • The withdrawFees function strictly requires the contract's overall ETH balance to exactly equal totalFees.

  • Anyone can forcefully send ETH to a contract bypassing receive() or enterRaffle() functions by utilizing selfdestruct.

// Root cause in the codebase with @> marks to highlight the relevant section
function withdrawFees() external {
@> require(address(this).balance == uint256(totalFees), "PuppyRaffle: There are currently players active!");
// ...
}

Risk

Likelihood:

  • High. An attacker only needs to sacrifice 1 wei to permanently trigger this lock.

Impact:

  • The withdrawFees function will always fail, permanently locking any legitimately collected protocol fees inside the contract.

Proof of Concept

// Demonstrated in `test_donationAttackWithdrawFees()` within `PuppyRaffleAudit.t.sol`
// Attacker calls: selfdestruct(payable(puppyRaffleAddress)) with 1 wei.
// This sets address(this).balance > totalFees making the require physically impossible to pass.

Recommended Mitigation

Check if the balance is strictly greater-than/equal-to, or track active players safely.

function withdrawFees() external {
- require(address(this).balance == uint256(totalFees), "PuppyRaffle: There are currently players active!");
+ // Alternatively, check `players.length == 0` to verify players are not active
+ require(address(this).balance >= uint256(totalFees), "PuppyRaffle: Insufficient balance!");
Updates

Lead Judging Commences

ai-first-flight-judge Lead Judge about 2 hours ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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

Give us feedback!