The on-chain data is public, and anyone has access to that data. Predicting a winner based on that data can be vulnerable and could be exploited by a malicious user to always select themselves as the winner of the raffle.
PuppyRaffle.sol - Lines 128 - 129;
The protocol generates a hash using on-chain data (msg.sender, block.timestamp, and block.difficulty) and takes the modulo of that hash after converting it into a uint256 value.
Any contract can access all of these values and the total number of players in the PuppyRaffle contract to predict the winner's index. Since the PuppyRaffle::selectWinner is callable by any user, a malicious user can predict these values and call the PuppyRaffle::selectWinner function only when their deployed contract will be the winner of the raffle.
Add this test inside PuppyRaffleTest.t.sol file;
In this test, a malicious user calls the PuppyRaffle::enterRaffle function and passes the address of his contract as a player in the raffle. After the raffle duration ends, he start predicting the winner index. Once he found the index of his contract as the winner, he calls the PuppyRaffle::selectWinner function and won the raffle.
I added this function inside PuppyRaffle contract to get the total number of players for convenience, but even without adding this function, any user can know the total number of players.
Import and inherit the IERC721Receiver and add onERC721Received function inside the contract to make our test contract compatible to receive the ERC721 tokens.
Run the test using command forge test --match-test test_HackRandomness -vv
Output
Anyone can steal the NFT as shown in the Poc.
Similarly, anyone can predict the rarity of the NFT and mint the LEGENDARY everytime.
Manual Review, foundry
Do not use the on-chain data for randomness.
Root cause: bad RNG Impact: manipulate winner
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.