Cross-Contract Taint Flow
Detects tainted data flowing from untrusted external calls into security-critical operations like access control checks or transfer amounts.
Cross-Contract Taint Flow
Overview
The cross-contract taint detector tracks data flowing from external call return values into security-sensitive operations. When a contract uses the return value of an untrusted external call as input to access control decisions, transfer amounts, or storage keys without validation, an attacker controlling the external contract can manipulate the calling contract’s behavior.
Why This Is an Issue
External calls return attacker-controlled data when the target is user-supplied or upgradeable. If this data flows into:
- Access control:
require(externalContract.isAdmin(msg.sender))— attacker’s contract always returns true - Transfer amounts:
token.transfer(user, oracle.getPrice())— attacker’s oracle returns inflated prices - Storage indices:
data[externalContract.getIndex()]— attacker controls which slot is read/written
The Poly Network attack ($611M, 2021) exploited cross-contract data flow where return values from a user-controlled contract were used in authentication decisions.
How to Resolve
// Before: Trusts return value from user-supplied contract
function withdraw(address oracle, uint256 id) external {
uint256 amount = IOracle(oracle).valueOf(id); // Tainted
token.transfer(msg.sender, amount); // Attacker controls amount
}
// After: Use allowlisted oracle, validate returned value
function withdraw(uint256 id) external {
require(allowedOracles[oracle], "Untrusted oracle");
uint256 amount = IOracle(trustedOracle).valueOf(id);
require(amount <= maxWithdrawal, "Amount exceeds limit");
token.transfer(msg.sender, amount);
}
Examples
Sample Sigvex Output
{
"detector_id": "cross-contract-taint",
"severity": "medium",
"confidence": 0.70,
"description": "Return value from external call to user-supplied address at offset 0x5c flows into transfer amount at offset 0x8a without validation. Attacker can control the transferred amount.",
"location": { "function": "withdraw(address,uint256)", "offset": 92 }
}
Detection Methodology
- Taint source identification: Marks return values from CALL/STATICCALL to non-constant addresses as tainted.
- Taint propagation: Tracks tainted values through arithmetic operations, memory copies, and variable assignments.
- Sink detection: Identifies security-sensitive sinks: CALL value parameters, SSTORE, conditional jumps guarding access control.
- Sanitizer recognition: Removes taint when values pass through validation (range checks, allowlist lookups).
Limitations
- Inter-procedural taint analysis is bounded; taint flowing through multiple internal function calls may be lost.
- Allowlisted contracts that are themselves upgradeable remain tainted but may not be flagged.
- Assembly blocks that copy return data via RETURNDATACOPY may break taint tracking.
Related Detectors
- Oracle Manipulation — oracle price manipulation
- Arbitrary External Calls — arbitrary external call targets
- Input Validation — missing parameter validation