The root of the problem are in refund who makes a call to refund the entrance fee back to player. With this call the recipient can execute an reentrancy attack calling several times the sendValue function to steal funds and take advantage of the protocol.
The implementation of refund does not strictly follow the Checks-Effects-Interactions (CEI) pattern as it is updating the address after sending out the entranceFee.
L96-L105 uses sendValue function from Address.sol which clearly says as the control is transferred to 'recipient' care must be taken to not create reentrancy vulnerabilities.
internally sendValue uses call function to send entranceFee back to the player which brings a possiblity to make a reentrancy attack . If the player is not an EOA but a contract and is set up to immediately call refund again as part of its fallback function. Since the state of the players array hasn't been updated yet (the attacker's address hasn't been set to address(0)), contract thinks attacker is still a valid player and sends the entrance fee again. This can be repeated as many times as the attacker wants (or until the contract runs out of gas), draining the contract of its funds.
Manual Review
One possibility is to add nonReentrant guard to refund function.
Another, Implement the CEI pattern in refund function by updating player address to address(0) before making external calls.
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.