TwentyOne

First Flight #29
Beginner FriendlyGameFiFoundrySolidity
100 EXP
View results
Submission Details
Severity: low
Invalid

Fail to handle edge Case: PlayersHand >21 && DealersHand >21.

Summary

The contract logic does not correctly handle scenarios where both the Dealer's hand and the Player's hand exceed 21 simultaneously. In the current implementation, the Player automatically wins if the Dealer's hand exceeds 21, regardless of the Player's own hand total.

Vulnerability Details

When both the Player's hand and the Dealer's hand exceed 21, the contract does not check the Player's hand before determining the winner. Instead, it assumes the Player wins as long as the Dealer's hand exceeds 21. This oversight leads to situations where the Player can exploit this logic to win games they should lose.

PoC

Please include the following in the TwentyOne.sol contract.

// add "mapping(address => bool) private gameResults;"
mapping(address => PlayersCards) playersDeck;
mapping(address => DealersCards) dealersDeck;
+ mapping(address => bool) private gameResults; // just added.
function endGame(address player, bool playerWon) internal {
delete playersDeck[player].playersCards; // Clear the player's cards
delete dealersDeck[player].dealersCards; // Clear the dealer's cards
delete availableCards[player]; // Reset the deck
if (playerWon) {
payable(player).transfer(2 ether);
+ gameResults[player] = playerWon;
emit FeeWithdrawn(player, 2 ether); // Emit the prize withdrawal event.
}
}
+ function getGameResult(address player) public view returns (bool) {
+ return gameResults[player];
+ }

Add this function to the test file.

function testCallwhenBoth22() public {
vm.deal(address(twentyOne), 20 ether);
vm.startPrank(player1);
// Start the game
twentyOne.startGame{value: 1 ether}();
// Mock dealer's and player's hands to both equal 22
vm.mockCall(
address(twentyOne),
abi.encodeWithSignature("dealersHand(address)", player1),
abi.encode(22) // Dealer's hand total is 22
);
console.log("here is the dealer's hand", twentyOne.dealersHand(player1));
vm.mockCall(
address(twentyOne),
abi.encodeWithSignature("playersHand(address)", player1),
abi.encode(22) // Player's hand total is 22
);
console.log("here is the player's hand", twentyOne.playersHand(player1));
twentyOne.call();
bool gameResult = twentyOne.getGameResult(player1);
assertEq(gameResult, true, "Expected player to lose the game");
vm.stopPrank();
}

Result:

Ran 1 test for test/TwentyOne.t.sol:TwentyOneTest
[PASS] testCallwhenBoth22() (gas: 1176837)
Logs:
here is the dealer's hand 22
here is the player's hand 22
playerWon is equal: true
Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 24.09ms (21.41ms CPU time)
```

Impact

Incorrect Winner Determination: Players win games they should lose or draw, undermining the contract's integrity.

Tools Used

  • Foundry/Hardhat for mocking and testing.

  • Console Logging for debugging and validating intermediate states.

Recommendations

Modify the call function to explicitly check both the Dealer's and the Player's hands before deciding the outcome. i.e.
if (playerHand > 21 && dealerHand > 21)

Updates

Lead Judging Commences

inallhonesty Lead Judge
11 months ago
inallhonesty Lead Judge 11 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

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