Oracle Manipulation Exploit Generator
Sigvex exploit generator that validates spot-price oracle manipulation by comparing contract execution under normal vs. artificially imbalanced DEX reserve configurations.
Oracle Manipulation Exploit Generator
Overview
The oracle manipulation exploit generator validates findings from the oracle_manipulation detector by simulating a DEX pool reserve imbalance and checking whether the contract’s behavior changes in response to artificially manipulated pool prices. A contract that reads spot prices directly from a DEX without TWAP protection will behave differently when the reserve ratio is skewed, confirming exploitability.
Oracle manipulation attacks are a class of economic exploit distinct from code bugs — the protocol’s logic is correct, but the price data it trusts can be temporarily falsified within a single transaction using flash loans or large swaps.
Note: Exploit generation in Sigvex is for vulnerability validation purposes only.
Attack Scenario
- Setup: The attacker identifies a protocol that computes a critical value (collateral ratio, swap output, liquidation threshold) from a DEX pool’s
reserve0/reserve1ratio. - Trigger: The attacker performs a large swap (or uses a flash loan) to drain
reserve0from1_000_000to100_000tokens while inflatingreserve1from1_000_000to10_000_000— a 10:1 imbalance. The spot pricereserve1 / reserve0shifts from 1.0 to 100.0. - Exploitation: The attacker calls the vulnerable function. The contract queries
dex_pool.getReserves()and computes a price based on the manipulated ratio. For a lending protocol, this inflates collateral value; for a DEX aggregator, it produces a favorable swap rate. - Impact: The attacker extracts value (borrows more than collateral supports, receives more tokens than fair value) and then reverses the trade to restore the pool ratio, repaying any flash loan in the same transaction.
Exploit Mechanics
The generator configures two world states for a simulated DEX pool contract:
Normal reserves (Scenario 1):
reserve0(storage slot 8):1_000_000reserve1(storage slot 9):1_000_000- Price ratio: 1:1
Manipulated reserves (Scenario 2):
reserve0(storage slot 8):100_000(drained to 10%)reserve1(storage slot 9):10_000_000(inflated 10x)- Price ratio: 100:1
The calldata targets the getPrice() function (selector 0x98d5fdac) unless a specific selector is available from the finding location.
Verdict logic:
| Normal result | Manipulated result | Finding |
|---|---|---|
| Succeeded | Reverted | Confirmed vulnerable (confidence 0.75): contract rejects manipulated price, meaning it validates — but not in the right direction |
| Reverted | Succeeded | Confirmed critical (confidence 0.75): behavior inverted by price manipulation |
| Both succeeded | Same gas ± tolerance | Likely vulnerable (confidence 0.60): no apparent slippage protection |
| Both reverted | — | Inconclusive |
The generated PoC illustrates the attack structure:
contract OracleManipulator {
function attack() external {
// 1. Flash loan large amount of token0
// 2. Swap to manipulate pool reserves (drain reserve0, inflate reserve1)
// 3. Call vulnerable contract that reads manipulated spot price
// 4. Profit from inflated price
// 5. Reverse swap to restore pool
// 6. Repay flash loan
}
}
Remediation
- Detector: Oracle Manipulation Detector
- Remediation Guide: Oracle Manipulation Remediation
Replace direct reserve reads with TWAP queries or external Chainlink feeds:
// VULNERABLE: Reads instantaneous pool reserves
function getCollateralValue(address token, uint256 amount) external view returns (uint256) {
(uint112 r0, uint112 r1,) = IUniswapV2Pair(pair).getReserves();
return amount * r1 / r0; // manipulable in a single tx
}
// SECURE: Uses Chainlink price feed with staleness check
function getCollateralValue(address token, uint256 amount) external view returns (uint256) {
(, int256 price,, uint256 updatedAt,) = chainlinkFeed.latestRoundData();
require(block.timestamp - updatedAt <= MAX_STALENESS, "Stale price");
return amount * uint256(price) / 1e8;
}