Skip to content

Token Approval Without Spending Limits

id: LS45M
title: Token Approval Without Spending Limits
baseSeverity: M
category: approvals
language: solidity
blockchain: [ethereum]
impact: Unlimited asset drain by approved party
status: draft
complexity: low
attack_vector: external
mitigation_difficulty: easy
versions: [">0.6.0", "<0.8.25"]
cwe: CWE-275
swc: SWC-114

πŸ“ Description

  • Token approval without spending limits occurs when a user or contract approves another address (e.g., DEX, vault, spender) for the maximum uint256 value (2^256 - 1), rather than a limited or session-specific amount.
  • This practice, while common for gas optimization, introduces a major risk:
  • If the approved address is compromised, the attacker can:
  • Drain all approved token balances at once,
  • Front-run or race the intended use,
  • Exploit long-lived approvals across unrelated sessions or dApps.
  • This vulnerability has led to repeated losses in DeFi phishing attacks, compromised frontends, and wallet integrations.

🚨 Vulnerable Code

function approveUnlimited(IERC20 token, address spender) external {
    token.approve(spender, type(uint256).max); // ❌ Dangerous unlimited approval
}

πŸ§ͺ Exploit Scenario

Step-by-step attack:

  1. A user approves a DEX contract with 2^256 - 1 allowance.
  2. Later, that DEX's contract is compromised or a malicious upgrade is pushed.
  3. The attacker calls transferFrom(user, attacker, entire balance) using the lingering allowance.
  4. User’s tokens are drained instantly, with no further signature or interaction required.

Assumptions:

  • The user or contract gives unlimited allowance.
  • The spender is compromised or malicious in the future.

βœ… Fixed Code

function safeApprove(IERC20 token, address spender, uint256 amount) external {
    require(amount > 0 && amount <= token.balanceOf(msg.sender), "Invalid amount");
    token.approve(spender, amount); // βœ… Approve only what’s needed
}

🧭 Contextual Severity

- context: "Default"
  severity: M
  reasoning: "Common approval pattern but depends on the safety of the approved contract."
- context: "DeFi protocol with router approvals"
  severity: H
  reasoning: "Widespread use of infinite approval multiplies blast radius."
- context: "Private wallet use with trusted spender"
  severity: L
  reasoning: "Risk minimal if spender is secure and trusted."

πŸ›‘οΈ Prevention

Primary Defenses

  • Avoid approve(spender, type(uint256).max) unless absolutely required and trusted.
  • Approve only the amount needed for a specific transaction.
  • Use ERC-20 permit() (EIP-2612) for gasless, single-use approvals where possible.

Additional Safeguards

  • Implement approval race mitigation by setting allowance to zero before updating.
  • Add expiration deadlines or nonces to time-limit approvals.
  • Track and revoke stale or unused allowances in off-chain monitoring systems (e.g., revoke.cash).

Detection Methods

  • Slither: unlimited-approval, unsafe-token-approve, approve-max detectors.
  • Manual audit of all approve(...) logic in contracts and dApp integration flows.
  • Integration testing to ensure minimum necessary approvals are enforced.

πŸ•°οΈ Historical Exploits

  • Name: Badger DAO Exploit
  • Date: December 2021
  • Loss: Approximately $120 million
  • Post-mortem: Link to post-mortem

πŸ“š Further Reading


βœ… Vulnerability Report

id: LS45M
title: Token Approval Without Spending Limits 
severity: M
score:
impact: 5         
exploitability: 3 
reachability: 4   
complexity: 1     
detectability: 5  
finalScore: 4.1

πŸ“„ Justifications & Analysis

  • Impact: High β€” full balance loss is possible with one bad approval.
  • Exploitability: Depends on spender being compromised or malicious, which is common in phishing.
  • Reachability: Very common in DeFi frontends and wallet interactions.
  • Complexity: Trivial fix β€” just approve the needed amount.
  • Detectability: High β€” easily spotted in audits and flagged by tools.