The function ChoosingRam::selectRamIfNotSelected depends on a random value to select the participant to be selected as Ram. This function generates the random number by using block.timestamp and block.prevrandao values. Those values are considered a bad source of randomness. The organiser can predict the outcome and execute the function only if the desired user will become Ram. The selected Ram will take the reward.
Using block.timestamp as a source of randomness is commonly advised against, as the outcome can be manipulated by calling contracts. Also, for some chains like zkSync block.prevrandao is a constant value. This will allow the users to predict the result of the calculated number in Line 90 of ChoosingRam.sol: uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao))) % ramNFT.tokenCounter();. This will give the organiser the opportunity to break the random selection of the Ram and to select a specific user who will collect the reward.
The following code demonstrates how an attack can be executed.
The bad source of randomness gives the opportunity to the organiser to select the specific user which will become Ram and which will get the reward. Although, the organiser is considered to be trusted, the desired logic of the contract is not implemented correctly. The random number is not really a random number.
Manual review
Consider using a decentralized oracle for the generation of random numbers, such as Chainlinks VRF. The Chainlink VRF gives two methods to request randomness: subscription and direct funding method. They will have their added cost, but will solve the randomness issues of the Dussehra contract.
The organizer is trusted, but the function `ChoosingRam::selectRamIfNotSelected` uses a way to generate a random number that is not completely random.
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.