Rock Paper Scissors

First Flight #38
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: high
Invalid

Contract allows players to submit `bytes32(0)` as commit hash

Summary

The RockPaperScissors contract allows players to submit bytes32(0) as a valid commit hash, while simultaneously using the same value (bytes32(0)) as a sentinel to check if a player has already committed. This creates a critical ambiguity in the contract state and opens the door to multiple exploitation scenarios, including denial of service attacks and game state manipulation.

Vulnerability Details

In the commitMove function, the contract uses bytes32(0) to check if a player has already committed:

if (msg.sender == game.playerA) {
require(game.commitA == bytes32(0), "Already committed");
game.commitA = _commitHash;
} else {
require(game.commitB == bytes32(0), "Already committed");
game.commitB = _commitHash;
}

However, the function never validates that _commitHash cannot be bytes32(0), meaning a player can submit bytes32(0) as a valid commit value.

Additionally, in the first turn logic check:

if (game.currentTurn == 1 && game.commitA == bytes32(0) && game.commitB == bytes32(0)) {
// First turn, first commits
require(game.playerB != address(0), "Waiting for player B to join");
game.state = GameState.Committed;
}

The function assumes that bytes32(0) means "not committed yet," creating a situation where the contract cannot distinguish between a player who has not committed yet and a player who has committed the value bytes32(0).

When the _determineWinner function resets for the next turn, it does the following:

game.commitA = bytes32(0);
game.commitB = bytes32(0);

This creates further ambiguity for subsequent turns.

Impact

Critical severity. This vulnerability enables multiple attack vectors:

  1. Game State Confusion: A player deliberately submitting bytes32(0) as their commit hash creates a state where the contract logic cannot determine if they've committed.

  2. Denial of Service: A malicious player could repeatedly submit bytes32(0) as their commit hash, causing the game to behave unexpectedly or preventing proper state transitions.

  3. Game Stalling: The game could get stuck in the first turn logic branch, as it would always see commitA and commitB as bytes32(0) even after commits have been made.

  4. Revelation Manipulation: A player who committed bytes32(0) could potentially manipulate the reveal phase since they can claim to have either committed or not committed.

In the worst case, this vulnerability completely breaks the commit-reveal pattern that is fundamental to the Rock Paper Scissors game's fairness.

Tools Used

  • Manual code review

Recommendations

  1. Explicitly reject zero-hash commits:

function commitMove(uint256 _gameId, bytes32 _commitHash) external {
require(_commitHash != bytes32(0), "Invalid commit: cannot use zero hash");
// Rest of the function
}
Updates

Appeal created

m3dython Lead Judge 2 months ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.