Arbitrary from in transferFrom
id: LS17C
title: Arbitrary from in transferFrom
baseSeverity: C
category: authorization-bypass
language: solidity
blockchain: [ethereum, polygon, bsc, arbitrum, optimism]
impact: Unauthorized token transfer from arbitrary user accounts
status: draft
complexity: low
attack_vector: external
mitigation_difficulty: easy
versions: [">=0.4.0", "<=0.8.25"]
cwe: CWE-285
swc: SWC-114
📝 Description
- The transferFrom() function in ERC-20 or ERC-721 tokens is designed to transfer tokens on behalf of another address, but it must validate that the caller is authorized to do so (i.e., msg.sender has a sufficient allowance from from).
- If a custom token implementation does not enforce proper access control on the from parameter, it allows any user to drain tokens from any address, resulting in theft of assets.
- This bug typically occurs when the contract checks only balances[from] without validating allowance[from][msg.sender].
🚨 Vulnerable Code
function transferFrom(address from, address to, uint256 amount) public returns (bool) {
require(balances[from] >= amount, "Insufficient balance");
balances[from] -= amount;
balances[to] += amount;
return true; // ❌ missing allowance check
}
🧪 Exploit Scenario
Step-by-step exploit process:
- A custom ERC-20 token contract is deployed with the above transferFrom() implementation.
- Alice holds 1,000 tokens but has not approved anyone to spend her tokens.
- Attacker calls transferFrom(alice, attacker, 1_000) directly.
- The contract checks Alice’s balance but not whether the attacker is allowed to use it.
- Tokens are transferred from Alice to the attacker without consent.
Assumptions:
- The token uses a broken transferFrom() implementation.
- msg.sender is not validated as being approved by from.
- Victims have token balances in the vulnerable contract.
✅ Fixed Code
function transferFrom(address from, address to, uint256 amount) public returns (bool) {
require(balances[from] >= amount, "Insufficient balance");
require(allowances[from][msg.sender] >= amount, "Not approved");
allowances[from][msg.sender] -= amount;
balances[from] -= amount;
balances[to] += amount;
return true;
}
🧭 Contextual Severity
- context: "Core ERC-20 token logic lacks allowance checks"
severity: C
reasoning: "Anyone can drain any user’s balance—catastrophic financial loss."
- context: "ERC-20 wrapper or internal logic used by known dApps"
severity: H
reasoning: "Still very dangerous; attacker may exploit via integrations."
- context: "transferFrom() unused, or only called internally with trusted addresses"
severity: M
reasoning: "Risk depends on usage context; still a dangerous pattern to leave in code."
🛡️ Prevention
Primary Defenses
- Always validate that msg.sender is approved via allowance[from][msg.sender].
- Use OpenZeppelin’s ERC-20 or ERC-721 contracts.
- Never reinvent token logic unless necessary and reviewed.
Additional Safeguards
- Add tests that simulate transfers from unauthorized addresses.
- Include fail-safe ownership guards on custom transfer logic.
- Audit external calls that pass user addresses as parameters.
Detection Methods
- Slither: missing-authorization, custom-erc20-transferfrom
- Manual review of transferFrom() implementations
- Static assertions: check presence of allowance validation
🕰️ Historical Exploits
- Name: Visor Finance vVISR Staking Contract Exploit
- Date: 2021-12-21
- Loss: Approximately 8.8 million
- Post-mortem: Link to post-mortem
📚 Further Reading
- SWC-114: Authorization Bypass
- CWE-285: Improper Authorization
- OpenZeppelin ERC20 Reference
- Solidity Docs – Function Modifiers
✅ Vulnerability Report
id: LS17C
title: Arbitrary from in transferFrom
severity: C
score:
impact: 5
exploitability: 5
reachability: 5
complexity: 1
detectability: 4
finalScore: 4.75
📄 Justifications & Analysis
- Impact: Results in complete loss of user funds due to unauthorized token transfers.
- Exploitability: Easily exploitable by anyone calling the public function.
- Reachability: High—transferFrom() is designed to be public and widely used.
- Complexity: Low—attacker simply calls the function with arbitrary from.
- Detectability: Easily detectable by audit tools or during review, but frequently overlooked in custom token implementations.