Token-0x

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

Overlapping Memory & Storage Slots Cause Balance Corruption

Author Revealed upon completion

Root + Impact
Incorrect storage pointer handling in Yul allows:

  • overwriting user balances

  • overwriting allowances

  • breaking total supply

  • unexpected resets or corruption of token accounting

This can permanently destroy token state.

Description

  • ERC-20 stores balances as:

    balances[address] → uint256

    using keccak256(address . slot) for mapping access.


  • Token-0x uses manual Yul to compute mapping slots, but the implementation is missing shl(5, ...) or proper layout alignment. As a result, the mapping index overlaps with other storage variables.

// Root cause in the codebase with @> marks to highlight the relevant section
assembly {
// @> Incorrect mapping pointer calculation
// Uses the address directly instead of hashing with the slot
let slot := balances.slot // correct base slot
// @> WRONG: writing to address interpreted as storage index
sstore(add(slot, caller()), 100)
}

This breaks the invariant that mappings must be accessed via:

keccak256(key . slot)

Risk

Likelihood:

  • Triggered whenever balance/allowance is updated.

Any transfer using this broken pointer corrupts another user's balance.

Impact:

  • Balance overwrites allow minting, burning, or balance resets.

Approvals may corrupt unrelated storage (e.g., totalSupply).

Proof of Concept

contract Corrupt {
function corruptBalance(address token) external {
// A crafted transfer triggers the wrong storage pointer,
// overwriting an unrelated storage slot.
token.call(
abi.encodeWithSignature(
"transfer(address,uint256)",
address(0xDEAD),
123
)
);
}
}

After the call:

  • some random account may lose funds

  • the attacker may gain funds

  • totalSupply or allowances may become corrupted

Recommended Mitigation

Also ensure all mapping reads/writes use the same hashing pattern.

- remove this code
+ add this code
- // WRONG: using add(slot, key)
- sstore(add(slot, key), value)
+ // CORRECT: use hashed mapping storage slot
+ mstore(0x00, key)
+ mstore(0x20, slot)
+ let ptr := keccak256(0x00, 0x40)
+ sstore(ptr, value)

Support

FAQs

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

Give us feedback!