The intended behavior is that a user trades their own SantaToken balance for an additional NFT present. The README states that buyPresent trades 2e18 SantaTokens for an NFT, and the function comment says the caller must approve the SantasList contract to spend their SantaTokens.
The implementation instead accepts an arbitrary presentReceiver address, burns tokens from that address, and then mints the NFT to msg.sender. Since SantaToken.burn() can only be called by SantasList, the ERC20 allowance system is bypassed entirely once the call enters SantasList.buyPresent().
Likelihood:
This occurs whenever any user has a SantaToken balance and another address calls buyPresent(user).
EXTRA_NICE users receive SantaTokens through normal protocol flow, so eligible victim balances are expected to exist.
Impact:
Attackers can spend another user's SantaToken without approval.
Attackers receive the purchased NFT while the victim loses their SantaToken.
The protocol charges only 1e18 SantaToken even though the documented purchase cost is 2e18.
Add this test to test/unit/SantasListTest.t.sol:
The victim receives a SantaToken by being EXTRA_NICE. The attacker then calls buyPresent(victim), which burns the victim's token and mints the NFT to the attacker.
Burn tokens from the caller, require the intended purchase cost, and mint the NFT to the caller or to an explicit receiver depending on the intended UX. If ERC20 approval is intended, use transferFrom or a burn function that checks allowance.
The contest is live. Earn rewards by submitting a finding.
Submissions are being reviewed by our AI judge. Results will be available in a few minutes.
View all submissionsThe contest is complete and the rewards are being distributed.