Summary
The dealersHand function awards 10 points to only 3 out of the 13 possible card numbers, when according to classic blacjack, 10 points should be awarded to 4 out of 13 cards: The king, queen, jack and the number 10. Meanwhile, the playersHand function observes this rule, creating an unfair situation for both the player and the protocol as a loss could have been a win and vice versa.
Calculation of dealer's hand:
function dealersHand(address player) public view returns (uint256) {
uint256 dealerTotal = 0;
for (uint256 i = 0; i < dealersDeck[player].dealersCards.length; i++) {
uint256 cardValue = dealersDeck[player].dealersCards[i] % 13;
@> if (cardValue >= 10) {
dealerTotal += 10;
} else {
dealerTotal += cardValue;
}
}
return dealerTotal;
}
Calculation of player's hand:
function playersHand(address player) public view returns (uint256) {
uint256 playerTotal = 0;
for (uint256 i = 0; i < playersDeck[player].playersCards.length; i++) {
uint256 cardValue = playersDeck[player].playersCards[i] % 13;
@> if (cardValue == 0 || cardValue >= 10) {
playerTotal += 10;
} else {
playerTotal += cardValue;
}
}
return playerTotal;
}
Vulnerability Details
Proof of Concept:
Proof of Code:
Place the below code into TwentyOneTest in TwentyOne.t.sol
function testDealerHandIsCalculatedWrongly() public {
uint256[] memory playersCards = new uint256[]();
uint256[] memory dealersCards = new uint256[]();
playersCards[0] = 52;
playersCards[1] = 33;
dealersCards[0] = 13;
dealersCards[1] = 24;
uint256 playerHand = 0;
for (uint256 i = 0; i < playersCards.length; i++) {
uint256 cardValue = playersCards[i] % 13;
if (cardValue == 0 || cardValue >= 10) {
playerHand += 10;
} else {
playerHand += cardValue;
}
}
uint256 expectedDealerHand = 0;
for (uint256 i = 0; i < dealersCards.length; i++) {
uint256 cardValue = dealersCards[i] % 13;
if (cardValue == 0 || cardValue >= 10) {
expectedDealerHand += 10;
} else {
expectedDealerHand += cardValue;
}
}
uint256 actualDealerHand = 0;
for (uint256 i = 0; i < dealersCards.length; i++) {
uint256 cardValue = dealersCards[i] % 13;
if (cardValue >= 10) {
actualDealerHand += 10;
} else {
actualDealerHand += cardValue;
}
}
console.log("Player hand: ", playerHand);
console.log("Expected dealer hand: ", expectedDealerHand);
console.log("Actual dealer hand: ", actualDealerHand);
}
Impact
The wrong hand is calculated for the dealer, making each game unfair.
Tools Used
Foundry suite
Recommendations
function dealersHand(address player) public view returns (uint256) {
uint256 dealerTotal = 0;
for (uint256 i = 0; i < dealersDeck[player].dealersCards.length; i++) {
uint256 cardValue = dealersDeck[player].dealersCards[i] % 13;
- if (cardValue >= 10) {
+ if (cardValue == 0 || cardValue >= 10) {
dealerTotal += 10;
} else {
dealerTotal += cardValue;
}
}
return dealerTotal;
}