The cancelGame function is vulnerable to a reentrancy attack, allowing a malicious playerA to repeatedly call cancelGame during ETH refunds in _cancelGame. Since the game state remains GameState.Created after playerB joins, playerA can drain the contract's ETH, including playerB's bet, before playerB receives a refund.
The cancelGame function calls _cancelGame, which transfers ETH to playerA via .call. Without a reentrancy guard, a malicious playerA can reenter cancelGame during the transfer. The state remains GameState.Created after playerB joins, allowing playerA to cancel the game and exploit reentrancy to siphon multiple refunds, including playerB's funds.
Vulnerable Code:
Attack Scenario:
Malicious playerA creates a game with an ETH bet.
playerB joins, state stays GameState.Created.
playerA calls cancelGame, triggering _cancelGame.
During playerA's ETH refund, their receive function reenters cancelGame, draining additional ETH, including playerB's bet.
playerB receives no refund as the contract’s funds are depleted.
Financial Loss: playerA can steal playerB's bet and other contract funds via multiple refunds.
Reputation Damage: Exploits undermine contract trust.
Manual code review
Foundry for exploit simulation
Custom ReentrancyAttack contract
Add Reentrancy Guard:
Use OpenZeppelin’s ReentrancyGuard with nonReentrant on cancelGame.
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.