The RockPaperScissors::_finishGame
, RockPaperScissors::_handleTie
, and RockPaperScissors::_cancelGame
functions in the RockPaperScissors
contract use low-level call{value: ...}("")
operations combined with require(success, ...)
to distribute ETH prizes and refunds. If any recipient is a smart contract that rejects ETH transfers—by reverting in its receive()
or fallback()
function—these calls will fail. As a result, the entire function reverts, permanently preventing game finalization, refunds, or tie resolution. This creates a denial-of-service (DoS) vulnerability, where ETH becomes locked in the contract and core gameplay operations are halted.
The issue affects three critical internal functions:
Unprotected ETH Transfer
The low-level .call{value: ...}("")
pattern directly sends ETH without proper safeguards, leaving the contract vulnerable to reverts from malicious recipients.
Hard Revert with require(success, ...)
Each failed ETH transfer causes a hard revert, rolling back the entire function execution, making it impossible to continue or finish the current game action.
Permanent Denial-of-Service (DoS)
Malicious or improperly configured recipient contracts can intentionally or unintentionally lock ETH and permanently block game operations.
Affected Critical Functions
_finishGame
distributes prizes to the winner, _handleTie
refunds both players after a tie, and _cancelGame
refunds players upon cancellation.
All become vulnerable to permanent DoS scenarios upon ETH transfer failure.
Critical Consequences
Denial of Service (DoS): Malicious or faulty recipients permanently block prize distribution, refunds, or game cancellation, effectively freezing critical functions.
Locked Funds: ETH becomes irretrievably locked within the contract, causing permanent loss of liquidity and undermining user confidence.
Game Logic Freezing: Stalled games become permanently stuck in incomplete states (Finished
, Cancelled
), negatively impacting player experience and trust.
Admin Operations Blocked: Inability to finalize games or issue refunds may negatively affect administrative operations and fee collections.
Deploy a malicious contract (MaliciousReceiver
) with a receive()
function that deliberately reverts on receiving ETH.
Player A creates a game with an ETH bet, and the malicious contract joins the game.
Both parties commit and reveal moves, with the malicious player winning.
During _finishGame()
, the prize transfer fails, causing a revert.
Similar reverts can also occur in _handleTie
or _cancelGame
scenarios when refunding malicious contracts.
Manual Code Review
Foundry Unit Tests
Refactor to Pull-Payment Pattern:
Introduce a pull-payment approach where prize recipients or refund recipients explicitly withdraw funds. This avoids direct transfers, ensuring malicious or faulty recipients cannot trigger reverts.
Implement a withdraw function:
Update affected functions to use this mapping:
This recommended approach completely mitigates the DoS attack vector by deferring ETH transfers to user-initiated withdrawals.
Malicious player wins a game using a contract that intentionally reverts when receiving ETH, the entire transaction will fail
Malicious player wins a game using a contract that intentionally reverts when receiving ETH, the entire transaction will fail
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.