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

Cost to buy NFT via SantasList::buyPresent is 2e18 SantaToken but it burns only 1e18 amount of SantaToken

Summary

  • The cost to buy NFT as mentioned in the docs is 2e18 via the SantasList::buyPresent function but in the actual implementation of buyPresent function it calls the SantaToken::burn function which doesn't take any parameter for amount and burns a fixed 1e18 amount of SantaToken, thus burning only half of the actual amount that needs to be burnt, and hence user can buy present for their friends at cheaper rates.

  • Along with this the user is able to buy present for themselves but the docs mentions that present can be bought only for other users.

Vulnerability Details

The vulnerability lies in the code in the function SantasList::buyPresent at line 173 and in SantaToken::burn at line 28.

The function burn burns a fixed amount of 1e18 SantaToken whenever buyPresent is called but the true value of SantaToken that was expected to be burnt to mint an NFT as present is 2e18.

function buyPresent(address presentReceiver) external {
@> i_santaToken.burn(presentReceiver);
_mintAndIncrement();
}
function burn(address from) external {
if (msg.sender != i_santasList) {
revert SantaToken__NotSantasList();
}
@> _burn(from, 1e18);
}

PoC

Add the test in the file: test/unit/SantasListTest.t.sol.

Run the test:

forge test --mt test_UsersCanBuyPresentForLessThanActualAmount
function test_UsersCanBuyPresentForLessThanActualAmount() public {
vm.startPrank(santa);
// Santa checks user once as EXTRA_NICE
santasList.checkList(user, SantasList.Status.EXTRA_NICE);
// Santa checks user second time
santasList.checkTwice(user, SantasList.Status.EXTRA_NICE);
vm.stopPrank();
// christmas time 🌳🎁 HO-HO-HO
vm.warp(santasList.CHRISTMAS_2023_BLOCK_TIME());
// user collects their present
vm.prank(user);
santasList.collectPresent();
// balance after collecting present
uint256 userInitBalance = santaToken.balanceOf(user);
// now the user holds 1e18 SantaToken
assertEq(userInitBalance, 1e18);
vm.prank(user);
santaToken.approve(address(santasList), 1e18);
vm.prank(user);
// user buy present
// docs mention that user should only buy present for others, but they can buy present for themselves
santasList.buyPresent(user);
// only 1e18 SantaToken is burnt instead of the true price (2e18)
assertEq(santaToken.balanceOf(user), userInitBalance - 1e18);
}

Impact

  • Protocol mentions that user should be able to buy NFT for 2e18 amount of SantaToken but users can buy NFT for their friends by burning only 1e18 tokens instead of 2e18, thus NFT can be bought at much cheaper rate which is half of the true amount that was expected to buy NFT.

  • User can buy a present for themselves but docs strictly mentions that present can be bought for someone else.

Tools Used

Manual Review, Foundry Test

Recommendations

Include an argument inside the SantaToken::burn to specify the amount of token to burn and also update the SantasList::buyPresent function with updated parameter for burn function to pass correct amount of tokens to burn.

  • Update the SantaToken::burn function

-function burn(address from) external {
+function burn(address from, uint256 amount) external {
if (msg.sender != i_santasList) {
revert SantaToken__NotSantasList();
}
- _burn(from, 1e18);
+ _burn(from, amount);
}
  • Update the SantasList::buyPresent function

+ error SantasList__ReceiverIsCaller();
function buyPresent(address presentReceiver) external {
+ if (msg.sender == presentReceiver) {
+ revert SantasList__ReceiverIsCaller();
+ }
- i_santaToken.burn(presentReceiver);
+ i_santaToken.burn(presentReceiver, PURCHASED_PRESENT_COST);
_mintAndIncrement();
}
Updates

Lead Judging Commences

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

Price is not enforced in buyPresent

This line indicates that the intended cost of presents for naughty people should be 2e18: https://github.com/Cyfrin/2023-11-Santas-List/blob/6627a6387adab89ae2ba2e82b38296723261c08a/src/SantasList.sol#L87 PURCHASE_PRESENT_COST should be implemented to enforce the cost of presents.

buyPresent should send to presentReceiver

Support

FAQs

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