The selectWinner() function selects a winner by indexing into the players array at a pseudo-randomly chosen position. However, the refund() function zeroes out player slots instead of removing them from the array:
When selectWinner() later picks an index that corresponds to a refunded slot, winner is assigned address(0):
Sending ETH to address(0) via a low-level .call succeeds silently in Solidity — the ETH is not returned but simply destroyed. The entire prize pool (80% of all entrance fees) is permanently lost with no way to recover it.
Likelihood:
Any player who calls refund() creates a zero slot in the array, and selectWinner() does not skip zero slots
With many participants, the probability of landing on a zero slot is (refunded players) / (total players)
A griefing attacker can deliberately enter with many addresses, wait until near the end of the raffle, refund all of them, and maximize the probability of a zero-slot win
This requires no special technical knowledge — just calling refund() before selectWinner() is called
Impact:
The entire prize pool (80% of all ETH collected from entrance fees) is sent to address(0) and permanently destroyed
All honest players lose their entrance fees with no winner receiving the prize
An NFT is minted to address(0) making it unowned and unrecoverable
The previousWinner state variable is set to address(0), corrupting protocol state
Repeated attacks make the protocol completely unusable and cause indefinite loss of user funds
The Certora Prover rule prize_never_sent_to_zero was formally verified and returned VIOLATED, providing mathematical proof that previousWinner can be address(0) after selectWinner():
Replace the zeroing pattern in refund() with a swap-and-pop approach that removes the player from the array entirely, ensuring no zero slots ever exist:
Additionally, add a zero-address check in selectWinner() as a defense-in-depth measure:
## Description In the `selectWinner` function, when a player has refunded and their address is replaced with address(0), the prize money may be sent to address(0), resulting in fund loss. ## Vulnerability Details In the `refund` function if a user wants to refund his money then he will be given his money back and his address in the array will be replaced with `address(0)`. So lets say `Alice` entered in the raffle and later decided to refund her money then her address in the `player` array will be replaced with `address(0)`. And lets consider that her index in the array is `7th` so currently there is `address(0)` at `7th index`, so when `selectWinner` function will be called there isn't any kind of check that this 7th index can't be the winner so if this `7th` index will be declared as winner then all the prize will be sent to him which will actually lost as it will be sent to `address(0)` ## Impact Loss of funds if they are sent to address(0), posing a financial risk. ## Recommendations Implement additional checks in the `selectWinner` function to ensure that prize money is not sent to `address(0)`
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.