Token-0x

First Flight #54
Beginner FriendlyDeFi
100 EXP
Submission Details
Impact: high
Likelihood: high

Unchecked Arithmetic Overflow/Underflow in Assembly

Author Revealed upon completion

Root + Impact

Description

  • Solidity 0.8+ overflow checks do NOT apply inside assembly {} blocks.

  • The _burn() function performs subtraction without checking if the account has sufficient balance, causing underflow to type(uint256).max.

function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
let accountBalance := sload(accountBalanceSlot)
// @> NO CHECK: if value > accountBalance, this underflows!
sstore(accountBalanceSlot, sub(accountBalance, value))
// @> sub(0, 1) = 0xFFFF...FFFF = type(uint256).max
}
}

Risk

Likelihood:

  • Occurs when _burn() is called with value exceeding account balance

  • The Token contract exposes burn() publicly with no balance check

Impact:

  • Burning 1 token from a 0 balance creates type(uint256).max tokens (~1.16e77)

  • Complete destruction of token economics

  • Similar overflow risks exist in _mint() and _transfer() recipient balance

Proof of Concept

function test_BurnUnderflow() public {
address attacker = address(0xBEEF);
// Attacker has 0 tokens
assertEq(token.balanceOf(attacker), 0);
// Burn 1 token from 0 balance - should revert but doesn't!
token.burn(attacker, 1);
// Balance underflowed to max uint256
uint256 attackerBalance = token.balanceOf(attacker);
assertEq(attackerBalance, type(uint256).max);
console.log("Attacker balance:", attackerBalance);
// 115792089237316195423570985008687907853269984665640564039457584007913129639935
}

Recommended Mitigation

function _burn(address account, uint256 value) internal {
assembly ("memory-safe") {
let accountBalance := sload(accountBalanceSlot)
+ // Check balance before burning
+ if lt(accountBalance, value) {
+ mstore(0x00, 0xe450d38c) // ERC20InsufficientBalance selector
+ mstore(0x04, account)
+ mstore(0x24, accountBalance)
+ mstore(0x44, value)
+ revert(0x00, 0x64)
+ }
sstore(accountBalanceSlot, sub(accountBalance, value))
}
}

Support

FAQs

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

Give us feedback!