When a player
calls PuppyRaffle::refund
, the player's address is replaced with address(0)
. In enterRaffle()
, there is a check on players
as to whether a duplicate address exists and reverts if there is. If 2 or more players have refunded, address(0)
will exist multiple times in the players
array. Subsequent calls to enterRaffle()
will erroneously revert due to the duplicated refunded addresses, even if the array of address(s) provided to enterRaffle()
contain no duplicates. This is a denial of service (DoS) on entering the raffle.
In PuppyRaffle:enterRaffle
, when player(s) enters a raffle there is a check that no duplicate addresses exist on line 86-90:
However, when a player calls refund()
, their address is replaced with address(0)
in the players
array, as seen on line 103:
If >= 2 players call refund()
then address(0)
will exist in the array more than once and so enterRaffle()
will revert on every call resulting in a state of DoS.
DoS on calling enterRaffle()
means that attackers can increase their chance of winning by not allowing other players to enter the raffle.
Since address(0)
can win the raffle (see issue: "Zero address can win the lottery if a player has called PuppyRaffle::refund
") there is a situation in which no one can win the raffle if all active players call refund()
meaning that no other players can enter. selectWinner()
can still be called but address(0)
has 100% chance of winning since it occupies every seat in the raffle. This will result in a DoS as selectWinner()
will always revert as address(0)
cannot receive NFTs.
Since players being able to enter the raffle is a core capability of a raffle, hence, this is a high-severity vulnerability.
The following test enters two players into the raffle. Both players call refund()
and then both players attempt to enter the raffle again. If this test passes, enterRaffle()
will revert due to a duplicated address:
Running the test yields the following output, demonstrating that the test passes and players can no longer enter the raffle:
To mitigate this issue, include a second array that contains a mapping of address
to boolean
determining whether the player is active or not and set this boolean
when calling refund()
rather than setting the refunding address to address(0)
:
To refund
, the activityStatus
will be set to false
for the refunding address.
Since getActivePlayerIndex()
is only used externally so that the value can be used to call refund
, getActivePlayerIndex()
can be removed and refund
modified to take an address:
When drawing the winner, the players
activity status can be checked, and only allow active players to be drawn as the winner
.
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.