Since the random number generated to draw a card is based on block attributes and the address of the player,
it will remain the same for a player if both executions of the TwentyOne:drawCard function take place during the current time block.
This flaw lead to reveal great probabilities that the second card of the player is a 10.
This flaw occurs in the TwentyOne:startGame function, where both cards are drawn consecutively.
In the TwentyOne:drawCard function, the randomIndex variable is generated with pseudorandom mechanism block.prevrandao. Beyond the fact that this variable is predictible, it relies solely on block attributes, which remain unchanged during the current time block :
block.timestamp : Represents the timestamp when the block is mined.
block.prevrandao : Represents a pseudorandom value for one block, generated from data of the previous block.
msg.sender : Represents the address of the player.
This causes an issue on the randomness of the variable randomIndex, which can be the same for both cards.
Shortly, this flaw has an impact on the way cards are drawn, and I will explain how it works.
The availableCards is a mapping of an address with all the value of a cards game, so an array from 0, 1, 2... until 52.
Every computation on these values is modulo 13, so that we have the four colors of the game, from Ace to King.
When the random index is generated :
we get the card at the random index
In availableCards mapping, this value is replaced by the last value of the array which is obviously 52 for the first draw
then the last value is removed from the availableCards
This process assumes that if the index used for both draws is the same, then we will hit a 52 every time for the second card, which represents a 10 in TwentyOne game ( 52 % 13 = 0 => 10 in player hand, see TwentyOne:playerHand function)
This impact of this issue is limited, since it doesn't affect a loss of funds. This can affect the player's experience by changing the probability of winning, since he can surely have a 10 in his hand.
Nevertheless, this issue combined with the multiple issues on the randomness in this smart contract could make it easier for the attacker to withdraw funds.
Implement a solution where the random value is different on each call of the TwentyOne:drawCard function. This can be made by using Chainlink VRF.
Randomness Manipulation: The randomness mechanism relies on block.timestamp, msg.sender, and block.prevrandao, which may be predictable in certain scenarios. Consider using Chainlink VRF or another oracle for more secure randomness.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.