A malicious player can exploit how revealDeadline
is handled and not properly reset each turn to force automatic wins. By carefully timing the execution of commitMove()
, revealMove()
, and timeoutReveal()
, the attacker prevents the opponent from completing their move within the allowed timeframe, resulting in repeated timeout victories.
This behavior allows the attacker to:
Seize opponent stakes.
Illegitimately accumulate WinningToken
rewards.
Gain ETH
profits in wagered games, while the contract collects a 10% fee per exploited match.
The exploit is repeatable in any game, threatening both fair play and the protocol's economic balance.
Direct loss of funds for affected players.
Unfair inflation of WinningToken supply.
Systematic abuse without any counterplay for honest participants.
Each time this vulnerability is exploited in ETH games, the protocol automatically collects a 10% fee. This creates a clear conflict of interest, as the system profits from malicious behavior.
The attacker
and the innocent player
both start with a balance of 1 ETH
.
The attacker
creates a new game with an ETH
wager.
The innocent player joins the game.
Both players execute commitMove()
.
The innocent player
reveals their move using revealMove()
.
The attacker
waits until just before the revealDeadline
expires and then performs their revealMove().
The attacker
waits 1 second, then immediately performs a new commitMove()
, followed by revealMove()
, and finally calls timeoutReveal()
to force a win by timeout.
Attacker obte les recompenses com si haqués guanyat la partida.
Manual Review and Foundry
It is recommended to:
Reset the revealDeadline
to zero at the end of each turn to prevent reuse or manipulation in subsequent rounds.
Add a validation in timeoutReveal()
to ensure that the reveal phase has properly started before allowing a timeout claim.
This combined approach fully protects the game flow against both exploitation of the reveal timer and unintended behavior when the timer is uninitialized.
Note on Mitigation:
While the proposed fix effectively resolves the revealDeadline exploitation, it also exposes a latent design flaw:
Issue:
There is no commit phase timeout in the current contract logic.
After resetting revealDeadline each turn, if one player commits their move but the opponent refuses to respond (e.g., when losing), the game can become indefinitely frozen.
Without a commitDeadline, the active player cannot force game progression or claim victory.
This opens the door to griefing and locked funds scenarios.
Recommendation:
Introduce a commit phase timeout mechanism, similar to the reveal phase:
Start a commitDeadline when the first player commits.
Allow the active player to claim victory if the opponent fails to commit within the allowed time.
This ensures full protection against inactivity-based stalling tactics and maintains fair gameplay.
The contract does not enforce salt uniqueness
Attack allows a player to reveal their move for the next turn before the opponent commits
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.