Using strict equality in withdrawFees
function can cause accumulated fees to be forever stuck in the contract.
The require
function in withdrawFees
uses a strict equality check:
This check can be made to always fail by sending a small amount of ether directly to the smart contract address. This will make address(this).balance
part of the check be greater than the uint256(totalFees)
part of the check.
Even though the raffle contract only has one payable
function which also has a hard requirement on funds that can be sent, there are other ways funds can be sent to the contract.
This exploit uses the selfdestruct
function of a malicious contract. This contract receives funds from the attacker and then calls selfdestruct
and provides puppyRaffle
contract as the recipient of the ether.
This tiny amount of extra funds will cause address(this).balance
of puppyRaffle
contract to be greater than its uint256(totalFees)
amount and therefore the check below will always fail and funds will be locked.
require(address(this).balance == uint256(totalFees), "PuppyRaffle: There are currently players active!");
src/PuppyRaffle.sol
https://github.com/Cyfrin/2023-10-Puppy-Raffle/blob/07399f4d02520a2abf6f462c024842e495ca82e4/src/PuppyRaffle.sol#L158
Fee funds can not be withdrawn from the smart contract.
Do not use strict equality, instead use an inequality.
Manual Audit
Foundry
Create a new attacker contract below.
Note: this is just a POC contract. It is beyond scope of this POC to ensure this attack contract is written securely and that funds are handled correctly once the contract has them.
And the test function.
Note: this test function is to be added to the existing test suite as it needs already existing components from there.
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.