The withdrawWinnings()
function lacks an essential state check to ensure it is only callable when the game is in a concluded state. It is missing the gameEndedOnly
modifier, which is present on the resetGame()
function but was omitted here. This oversight allows the function to be called at any time, provided the caller has a non-zero balance in the pendingWinnings
mapping.
The scenario requires a winner from a previous round to not withdraw their prize immediately after the game ends. This is a plausible situation, as a user might forget, be slow to react, or an automated script could fail, leaving them with a pending balance when the next round begins.
This vulnerability breaks the intended state machine of the contract, which should clearly separate active and ended game rounds. While it does not allow a random attacker to steal funds, it allows a legitimate (but previous) winner to interfere with an active game. This can lead to race conditions, accounting confusion for off-chain services, and a violation of the game's core lifecycle integrity. It degrades the predictability and robustness of the contract.
The following test demonstrates the vulnerability. It simulates a scenario where the winner of Round 1 successfully withdraws their prize during the active phase of Round 2, a state in which withdrawals should be disabled.
Apply the gameEndedOnly
modifier to the withdrawWinnings()
function. This will enforce the correct state constraints, ensuring that prizes can only be withdrawn after a game has officially concluded and before a new one has begun.
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.