Flash Loan Attack Vector
Detects protocol patterns that are susceptible to flash loan price manipulation — specifically, reliance on on-chain spot prices that can be manipulated within a single transaction.
Flash Loan Attack Vector
Overview
Remediation Guide: How to Fix Flash Loan Attack Vectors
The flash loan detector identifies smart contract patterns that are susceptible to manipulation via uncollateralized flash loans. Flash loans allow an attacker to borrow large quantities of tokens at no cost within a single transaction, manipulate spot prices or reserves, exploit the protocol using the inflated or deflated prices, and repay the loan — all atomically.
The primary pattern Sigvex detects is reliance on balanceOf, token.reserve, or similar real-time state queries as pricing oracles. When a contract reads on-chain spot prices from a DEX pool and uses them to determine borrow amounts, collateral ratios, or liquidation thresholds, an attacker can skew those prices with a flash loan immediately before the sensitive read.
Why This Is an Issue
Flash loan attacks have caused hundreds of millions in losses. Notable incidents include: bZx ($600K and $350K in February 2020 — the first major flash loan exploits), Harvest Finance ($34M, October 2020), PancakeBunny ($45M, May 2021), Cream Finance ($130M, October 2021), Mango Markets ($117M, October 2022). The common thread is reliance on manipulable on-chain state as a price source.
A flash loan itself is not an attack — it is a legitimate DeFi primitive. The vulnerability is in the consuming protocol’s assumption that on-chain spot prices represent fair market value within a transaction.
How to Resolve
// Before: Vulnerable — uses current spot price from AMM reserve
function getTokenPrice(address token) internal view returns (uint256) {
(uint112 reserve0, uint112 reserve1,) = IUniswapV2Pair(pair).getReserves();
// VULNERABLE: spot price can be manipulated with flash loan
return (reserve1 * 1e18) / reserve0;
}
// After: Use a TWAP oracle instead of spot price
function getTokenPrice(address token) internal view returns (uint256) {
// Uses Uniswap V2 TWAP — computed over a time window, cannot be
// manipulated within a single block
uint256 price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
// ... TWAP computation over minimum 30-minute window
return computeTwap(price0Cumulative, windowStart, windowEnd);
}
For Uniswap V3 or Chainlink:
// Chainlink price feed — off-chain aggregated, manipulation-resistant
import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
AggregatorV3Interface internal priceFeed;
function getLatestPrice() public view returns (int256) {
(, int256 price, , uint256 updatedAt, ) = priceFeed.latestRoundData();
require(block.timestamp - updatedAt <= 3600, "Stale price"); // Use chainlink-staleness detector guidance
return price;
}
Examples
Vulnerable Code
contract VulnerableLendingPool {
IUniswapV2Pair public pair;
IERC20 public collateralToken;
// Collateral ratio based on instantaneous spot price
function calculateMaxBorrow(uint256 collateralAmount) public view returns (uint256) {
(uint112 reserve0, uint112 reserve1,) = pair.getReserves();
uint256 spotPrice = (reserve1 * 1e18) / reserve0; // MANIPULABLE
return (collateralAmount * spotPrice * 75) / (100 * 1e18);
}
function borrow(uint256 collateralAmount, uint256 borrowAmount) external {
uint256 maxBorrow = calculateMaxBorrow(collateralAmount);
require(borrowAmount <= maxBorrow, "Exceeds collateral value");
// Attacker: (1) flash loan inflates token price, (2) deposit small collateral,
// (3) borrow large amount using inflated price, (4) repay flash loan
collateralToken.transferFrom(msg.sender, address(this), collateralAmount);
_token.transfer(msg.sender, borrowAmount);
}
}
Fixed Code
contract SecureLendingPool {
AggregatorV3Interface public priceFeed; // Chainlink oracle
uint256 public constant MAX_PRICE_AGE = 3600; // 1 hour
function calculateMaxBorrow(uint256 collateralAmount) public view returns (uint256) {
(, int256 price, , uint256 updatedAt, ) = priceFeed.latestRoundData();
require(block.timestamp - updatedAt <= MAX_PRICE_AGE, "Stale oracle price");
require(price > 0, "Invalid price");
uint256 safePrice = uint256(price);
return (collateralAmount * safePrice * 75) / (100 * 1e8); // Chainlink uses 8 decimals
}
}
Sample Sigvex Output
{
"detector_id": "flash-loan",
"severity": "high",
"confidence": 0.72,
"description": "Function calculateMaxBorrow() uses getReserves() from a Uniswap V2 pair as a price oracle. This value can be manipulated within a transaction using a flash loan.",
"location": { "function": "calculateMaxBorrow(uint256)", "offset": 112 }
}
Detection Methodology
- DEX price call detection: Identifies calls to known DEX interfaces — Uniswap V2/V3
getReserves(),slot0(),observe(), BalancergetPoolTokens()— that return real-time pool state. - Price usage tracking: Follows the data-flow from these calls to determine whether the returned values influence borrow amounts, collateral ratios, liquidation parameters, or other financial calculations.
- Manipulation window detection: Checks whether there is any time-weighted averaging, TWAP computation, or multiple-block sampling that would make the price manipulation-resistant.
- Confidence scoring: Higher confidence when the spot price is directly used in a single arithmetic step; lower confidence when intermediate processing makes manipulation harder to confirm.
Limitations
False positives:
- Protocols that use spot prices only for informational display (not for financial calculations) may be flagged.
- TWAP implementations that do not use the standard Uniswap V2 accumulator pattern may not be recognized as manipulation-resistant.
False negatives:
- Flash loan susceptibility that occurs through multiple hops (e.g., price is read in a separate oracle contract) may not be detected without cross-contract analysis.
- Indirect manipulation via reserve-based pricing in AMMs with small liquidity may be missed.
Related Detectors
- Oracle Manipulation — broader oracle manipulation patterns
- Reentrancy — detects reentrancy vulnerabilities that can be combined with flash loans
- Slippage Validation — detects missing slippage protection exploitable via flash loans