Rock Paper Scissors

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

Missing Access control: Joining a game before starts can allow to replace player B if a wallet has a winning token or enough eth to deposit

Summary

PlayerB can be replaced if another user joins the game before it starts, causing that a user loses money if they have joined the game before and it did not get started yet, since they would be wiped out of the game and make it impossible for them to recover the token or the fees paid.

Vulnerability Details

No access control prevents any user (except playerA) with a winning token or enough eth to front-run and replace playerB before the first commit and the game start before the deadline, which would mean a playerB that gets replace loses their deposited money or token.

/**
* @notice Join an existing game with ETH bet
* @param _gameId ID of the game to join
*/
function joinGameWithEth(uint256 _gameId) external payable {
Game storage game = games[_gameId];
require(game.state == GameState.Created, "Game not open to join");
require(game.playerA != msg.sender, "Cannot join your own game");
require(block.timestamp <= game.joinDeadline, "Join deadline passed");
require(msg.value == game.bet, "Bet amount must match creator's bet");
game.playerB = msg.sender;
emit PlayerJoined(_gameId, msg.sender);
}
/**
* @notice Join an existing game with token
* @param _gameId ID of the game to join
*/
function joinGameWithToken(uint256 _gameId) external {
Game storage game = games[_gameId];
require(game.state == GameState.Created, "Game not open to join");
require(game.playerA != msg.sender, "Cannot join your own game");
require(block.timestamp <= game.joinDeadline, "Join deadline passed");
require(game.bet == 0, "This game requires ETH bet");
require(winningToken.balanceOf(msg.sender) >= 1, "Must have winning token");
// Transfer token to contract
winningToken.transferFrom(msg.sender, address(this), 1);
game.playerB = msg.sender;
emit PlayerJoined(_gameId, msg.sender);
}

Impact

The protocol would be vulnerable to griefing attacks (malicious attacks to let users lose money) or DOS attacks where a wallet maliciously joins all the time as playerB and renders the game unplayable.

Tools Used

Manual Review

Recommendations

Expand security checks to make sure playerB cannot be replaced that easily by any other player before starting the game.

Updates

Appeal created

m3dython Lead Judge 4 months ago
Submission Judgement Published
Validated
Assigned finding tags:

Absence of State Change on Join Allows Player B Hijacking

Game state remains Created after a player joins

Support

FAQs

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