Beginner FriendlyFoundry
100 EXP
View results
Submission Details
Severity: medium
Invalid

User can mint themselves another NFT after collecting present.

Summary

The documentation states that "An address is only allowed to collect 1 NFT per address, there is a check in the codebase to prevent someone from minting duplicate NFTs." This is invalid as the user of the protocol can mint themselves one more NFT by burning the token they receive from receiving presents for being EXTRA_NICE.

Vulnerability Details

The contract allows users to buyPresents for their friends at the price of 1e18 SantaToken through the following code

function buyPresent(address presentReceiver) external {
i_santaToken.burn(presentReceiver);
_mintAndIncrement();
}

The burn function is called from the buyPresent function which burns 1e18 SantaTokens.

function burn(address from) external {
if (msg.sender != i_santasList) {
revert SantaToken__NotSantasList();
}
_burn(from, 1e18);
}

A user can pass in their own address as the receiver which will burn their token and mint themself an NFT as they are the msg.sender as well as the receiver.

Proof of Concept for vulnerability

Overview:

The cost of buying a present is set to 1e18. This allows user to purchase NFTs at a lower price than what has been stated in the docs.

Actors:

  • User: The user will first redeem their presents for being EXTRA_NICE, this provides them with a NFT and a santaToken. They will burn the santaToken to mint themselves one more NFT.

  • Santa: Santa will check List1 and List2 for the User so they can redeem their rewards.

Working Test Case (if applicable):

function testBuyPresentWith1Token() public {
vm.startPrank(santa); // santa will first set the status of the victim as EXTRA_NICE
santasList.checkList(user, SantasList.Status.EXTRA_NICE);
santasList.checkTwice(user, SantasList.Status.EXTRA_NICE);
vm.stopPrank();
vm.warp(santasList.CHRISTMAS_2023_BLOCK_TIME() + 1);
vm.startPrank(user); // acting as the user
santasList.collectPresent(); // the user claims rewards
assertEq(santasList.balanceOf(user), 1); // the user has 1 NFT
assertEq(santaToken.balanceOf(user), 1e18); // the user has 1 Token
santasList.buyPresent(user); // user calls buyPresent passing in their own address
assertEq(santasList.balanceOf(user), 2); // user receives another NFT
assertEq(santaToken.balanceOf(user), 0); // user has 0 santaTokens
vm.stopPrank();
}

Impact

The disrupts the rules set by the protocol. The protocol states that only 1 NFT can be collected per address. A user can mint themselves 2 NFTs using this protocol. First by collecting rewards for being EXTRA_NICE and then burning the token they received in the rewards for a NFT.

Tools Used

Manual Review

Recommendations

Include a check to see if msg.sender != presentReceiver

function buyPresent(address presentReceiver) external {
if (msg.sender == presentReceiver) {
revert SantasList__AlreadyCollected();
}
i_santaToken.burn(presentReceiver);
_mintAndIncrement();
}
Updates

Lead Judging Commences

inallhonesty Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Design choice

Support

FAQs

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