ChainScore Labs
LABS
Guides

Exploitable Patterns in Token Contract Implementations

Chainscore © 2025
concepts

Core Vulnerability Categories

Critical security flaws in token contracts that attackers exploit to drain funds or manipulate protocol logic.

01

Access Control Flaws

Privilege escalation occurs when functions lack proper permission checks, allowing unauthorized users to perform admin actions.\n\n- Missing onlyOwner modifiers on critical functions like mint or pause.\n- Using tx.origin for authorization, which can be manipulated via a malicious contract.\n- This matters because it can lead to unlimited token minting or protocol shutdown by any user.

02

Arithmetic Issues

Integer overflows and underflows happen when arithmetic operations exceed a variable's storage limits, causing wrap-around behavior.\n\n- An unchecked balance - amount can underflow, granting a huge balance.\n- Batch transfer loops that miscalculate total sums can be exploited.\n- This matters as it can create tokens from nothing or lock user funds unexpectedly.

03

Reentrancy Attacks

State inconsistency is exploited by malicious contracts that call back into a vulnerable function before its state updates are complete.\n\n- The classic pattern involves a withdraw function that updates balances after sending ETH.\n- ERC-777 tokensReceived hooks can facilitate reentrancy in token contracts.\n- This matters because it allows attackers to drain contracts multiple times per transaction.

04

Logic & Business Rule Flaws

Incorrect validation of business rules or token economics can be manipulated for profit.\n\n- Fee-on-transfer tokens where the contract assumes a 1:1 transfer ratio.\n- Vesting contracts with flawed time-lock logic allowing early withdrawals.\n- This matters as it breaks core protocol assumptions, leading to unfair advantage or loss.

05

Oracle Manipulation

Price feed attacks target contracts that rely on external data sources for critical valuations.\n\n- Using a single DEX pool's spot price, which can be skewed with a flash loan.\n- Lack of circuit breakers or time-weighted average prices (TWAP).\n- This matters for lending protocols and synthetic assets, as it enables undercollateralized borrowing.

06

ERC Standard Non-Compliance

Deviations from expected behavior in ERC-20, ERC-721, or ERC-1155 can break integrations.\n\n- Returning false instead of reverting on failed transfers (non-standard).\n- Fee-on-transfer or rebasing tokens that cause balance discrepancies.\n- This matters because wallets, DEXs, and other contracts may handle tokens incorrectly, leading to locked funds.

Vulnerabilities by Token Standard

Common Implementation Flaws

ERC-20 is the foundational token standard, but its simplicity leads to exploitable patterns. The approve/transferFrom mechanism is a primary attack vector. A common mistake is failing to handle the race condition where a user changes an approval from 5 to 3 tokens, but an attacker front-runs the transaction to spend the original 5 before the reduction takes effect. This is mitigated by first setting approval to zero before setting a new value, a pattern now enforced by many wallets.

Critical Vulnerabilities

  • Incorrect balance updates: Early implementations sometimes updated balances before checking for sufficient funds, leading to integer underflows.
  • Missing return value checks: Not all ERC-20 tokens properly implement the bool return value; integrations that don't check it can fail silently.
  • Fee-on-transfer tokens: Tokens like STAKE deduct a fee on transfer, causing integrations like Uniswap pools to miscalculate input/output amounts if they assume the received balance equals the sent amount.

Example

A lending protocol like Compound that accepts any ERC-20 as collateral must explicitly handle fee-on-transfer tokens, or an attacker could deposit them and instantly borrow more than the net collateral received by the contract.

Methodology for Identifying Exploits

A systematic process for analyzing token contracts to uncover vulnerabilities.

1

Establish the Contract Context

Gather and analyze foundational contract information.

Detailed Instructions

Begin by obtaining the verified source code and ABI for the target token contract from a block explorer like Etherscan. Decompile the bytecode using tools like panoramix if source is unavailable. Identify the token standard (e.g., ERC-20, ERC-721) and map out the inheritance hierarchy and key dependencies, such as imported libraries or proxy implementations. Review the contract's creation and recent transaction history to understand its deployment context and typical usage patterns.

  • Sub-step 1: Retrieve contract address and source from Etherscan or Blockscout.
  • Sub-step 2: Examine the constructor arguments and initial state setup.
  • Sub-step 3: Catalog all public and external functions listed in the ABI.
  • Sub-step 4: Note any admin or owner privileges and associated addresses.
solidity
// Example: Checking for a common proxy pattern storage slot bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;

Tip: Use cast code <ADDRESS> from Foundry to fetch bytecode directly for analysis.

2

Analyze State Variable Layout and Access

Examine storage variables for unsafe visibility or incorrect mappings.

Detailed Instructions

Manually audit the declaration and usage of all state variables. Critical vulnerabilities often arise from public variables that should be private or internal, such as mapping structures storing balances or allowances. Scrutinize the storage layout for potential collisions, especially in upgradeable contracts using unstructured storage proxies. Verify that sensitive mappings (e.g., mapping(address => uint256) private _balances;) are indeed private and that any public getters do not expose unintended data. Check for the use of packed storage variables and ensure bit-level operations are safe from overflows.

  • Sub-step 1: List all state variables and their visibility modifiers.
  • Sub-step 2: Trace read/write access for each variable across all functions.
  • Sub-step 3: Identify any public mappings or arrays that could be manipulated.
  • Sub-step 4: Use slither-check-erc to detect standard conformance issues.
solidity
// Vulnerable example: Public mapping exposing internal logic mapping(address => uint256) public customBalances; // Risk: External writes may be possible.

Tip: Tools like hevm can interactively explore storage layouts for a given contract.

3

Trace External Call Flows and Reentrancy Vectors

Map all interactions with external addresses and contracts.

Detailed Instructions

Identify every call, delegatecall, staticcall, and external contract interaction (e.g., IERC20.transfer). The checks-effects-interactions pattern must be validated for each function. Manually trace the flow of execution to spot classic reentrancy vulnerabilities where state changes occur after an external call. Pay special attention to low-level .call{value:}() operations and functions that transfer tokens or ETH. Assess the trust assumptions of any integrated protocols or oracles; a malicious token contract received in a swap could callback into your function.

  • Sub-step 1: Use slither to generate a function call graph and identify external calls.
  • Sub-step 2: For each state-changing function, verify state updates happen before any external call.
  • Sub-step 3: Check for use of nonReentrant modifiers or equivalent mutex locks.
  • Sub-step 4: Review approve/transferFrom flows for ERC-20 race conditions.
solidity
// Example: Checking for unsafe call pattern function withdraw() public { (bool success, ) = msg.sender.call{value: address(this).balance}(""); // Interaction FIRST require(success); balances[msg.sender] = 0; // Effects AFTER - VULNERABLE }

Tip: Fuzz tests in Foundry can simulate malicious contracts re-entering your functions.

4

Audit Arithmetic Operations and Token Supply Logic

Review all mathematical operations for overflow/underflow and supply manipulation.

Detailed Instructions

Examine every arithmetic operation involving +, -, *, ++, --, and += or -=. While Solidity 0.8+ has default overflow checks, inline assembly or older compilers require manual SafeMath verification. Pay critical attention to the total supply mechanism in mintable/burnable tokens; an attacker may mint unlimited tokens if access control is flawed. Verify that functions like _mint and _burn correctly update both the recipient/destroyer balance and the _totalSupply state variable. Check for rounding errors in fee calculations or rebase logic that could be exploited over multiple transactions.

  • Sub-step 1: Use slither with the --detect overflow flag to flag risky operations.
  • Sub-step 2: Manually verify the logic in mint and burn functions for proper access control and supply updates.
  • Sub-step 3: Audit any fee-on-transfer or tax logic for discrepancies between transferred and received amounts.
  • Sub-step 4: Check for division before multiplication, which can lead to significant rounding down.
solidity
// Example: Safe arithmetic using Solidity 0.8+ function transfer(address to, uint256 amount) public returns (bool) { _balances[msg.sender] -= amount; // Built-in overflow check _balances[to] += amount; return true; }

Tip: Write invariant tests asserting totalSupply() always equals the sum of all balances.

5

Simulate Attack Vectors with Unit and Fuzz Testing

Construct and execute targeted tests to validate findings.

Detailed Instructions

Translate identified suspicious patterns into executable test cases using a framework like Foundry. Fuzz testing is essential for uncovering edge cases in input validation and state transitions. Write tests that simulate an attacker's actions: for example, a contract that re-enters during a transfer, or a user who front-runs an approve transaction. Use forge test --match-contract <TestName> -vvv for detailed traces. Create invariant tests (forge invariant) to check that core properties (e.g., totalSupply conservation) hold under random sequences of calls. Compare the contract's behavior against the official EIP specification.

  • Sub-step 1: For each potential vulnerability, write a testFail or test_Exploit function.
  • Sub-step 2: Use vm.prank and vm.deal to simulate attacks from malicious addresses.
  • Sub-step 3: Fuzz function parameters with @invariant and @fuzz annotations.
  • Sub-step 4: Run differential fuzzing against a known-correct reference implementation.
solidity
// Foundry test example for reentrancy function test_ReentrancyExploit() public { VulnerableToken token = new VulnerableToken(); AttackerContract attacker = new AttackerContract(address(token)); token.deposit{value: 1 ether}(); attacker.attack(); // Should fail if reentrancy guard works }

Tip: Leverage Foundry's cheatcodes to warp time, modify storage, and impersonate users for comprehensive testing.

Historical Exploit Case Studies

Analysis of major token contract vulnerabilities and their outcomes.

Vulnerability TypeExploit CasePrimary VectorLoss Amount (USD)Root Cause

Incorrect Fee-on-Transfer Logic

Fei Protocol Rari Fuse (2022)

Balance accounting error in balanceOf

80 million

Fee-on-transfer token integration without adjusting for deducted fees

Reentrancy on ERC-777 tokens

Uniswap/Lendf.Me (2020)

ERC-777 tokensToSend hook

25 million

Reentrant call during balance update in a non-reentrant guard function

Incorrect ERC20 decimals() Implementation

Bondly Finance (2021)

Proxy storage collision

5.8 million

Uninitialized proxy contract storing decimals at wrong storage slot

Signature Replay Across Forks

SushiSwap MISO (2021)

ERC20Permit signature reuse

3 million

Missing chain ID validation in permit message hashing

Flash Loan Price Manipulation

Harvest Finance (2020)

Oracle price manipulation via Curve pool

34 million

Reliance on a single DEX pool's spot price for value calculations

Insufficient Access Control on mint/burn

PolyNetwork (2021)

Unverified mint function caller

611 million

Missing owner/role validation on critical functions in a cross-chain contract

Integer Overflow in balanceOf

BEC Token (2018)

Batch transfer overflow

Vast supply inflation

Lack of SafeMath or compiler >=0.8; uint overflow in multiplication

Mitigation Strategies and Secure Patterns

Implement the Checks-Effects-Interactions pattern as a fundamental defense. First, perform all necessary condition checks (e.g., sufficient balance). Second, update all internal state variables (effects) before any external calls. Finally, make the external call to transfer tokens. For ERC-20, use the safeTransfer or safeTransferFrom functions from OpenZeppelin's SafeERC20 library, which revert on failure. Always set allowances to zero before updating them to a new value to prevent the allowance race condition. For example, when interacting with a DEX router, approve only the exact amount needed for the swap, not an infinite allowance.