Checks-Effects-Interactions order was not respected in the refund() function allowing the attacker to call refund() till he drains the contract's funds
Initial State: Players are entering the raffle.
Step 1: Attacker enters the raffle as well.
Step 2: Attacker calls the refund() function with his malicious contract by passing in argument its index using getActivePlayerIndex(address(this)).
Step 3: When refund() is called, the protocol makes the precious error of making an external call to the attacker's contract before updating the list of the players' address
Step 4: The attacker doesn't forget to add a receive() or fallback() function to his malicious contract that will keep on calling the refund() function till he drains the prtocol's contract from all its funds.
The protocol will lose all its funds. The players that entered the same raffle as the attacker are victims of this exploit as players cannot get refunded anymore, the owner will not get his fees and the winner will not get his prize amount.
Manual analysis
payable(msg.sender).sendValue(entranceFee);
players[playerIndex] = address(0);
players[playerIndex] = address(0);
payable(msg.sender).sendValue(entranceFee);
reentrancy in refund() function
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.