Install in Claude Code
Copygit clone --depth 1 https://github.com/PurpleAILAB/Vigilo /tmp/economic-attack && cp -r /tmp/economic-attack/packages/opencode/skills/economic-attack ~/.claude/skills/economic-attackThen start a new Claude Code session; the skill loads automatically.
Definition
SKILL.md
# Economic Attack Vulnerability Analysis
**OWASP SC02/SC07:2025** - Oracle Manipulation (#2) and Flash Loan Attacks (#7) in OWASP SC Top 10 (2025). Flash loans enable **60%+ of DeFi exploits**, with 2024-2025 major attacks including Cetus ($223M), Euler ($197M), KiloEx ($117M). Price manipulation accounts for **34%+ of market manipulation exploits**.
---
## Attacker Mindset: Infinite Capital
**CRITICAL**: With flash loans, attackers have **INFINITE CAPITAL** for one transaction.
```
+------------------------------------------------------------------+
| SINGLE ATOMIC TRANSACTION |
+------------------------------------------------------------------+
| 1. BORROW | Flash loan $100M+ from Aave/dYdX (cost: 0.09%) |
| 2. MANIPULATE| Change any on-chain value (price, balance, ratio) |
| 3. EXPLOIT | Call target function with manipulated state |
| 4. PROFIT | Extract value (mint, borrow, swap at bad rate) |
| 5. REPAY | Return flash loan + fee |
| 6. KEEP | Attacker keeps profit, victims lose funds |
+------------------------------------------------------------------+
```
**Key insight**: If ANY step fails, entire transaction reverts. Attacker loses only gas (~$50).
This means attackers can try complex attacks with zero risk.
---
## Why Economic Attacks Happen (Root Causes)
### Root Cause 1: Single Source Price Dependency
Protocol trusts ONE price source that attacker can manipulate.
```solidity
// VULNERABLE: Single DEX pool as price source
function getPrice() public view returns (uint256) {
(uint112 r0, uint112 r1,) = uniswapPair.getReserves();
return uint256(r1) * 1e18 / uint256(r0); // @audit Flash loan can drain r0
}
```
**Attacker's view**: "I can move this price with enough capital. Flash loan gives me that capital."
### Root Cause 2: Spot Price Trust
Using current-moment values instead of time-averaged values.
```solidity
// VULNERABLE: Current block's reserves
price = reserves[1] / reserves[0]; // @audit Reflects THIS transaction's state
// What attacker sees:
// 1. Swap to move reserves
// 2. Read manipulated price
// 3. Exploit protocol
// 4. Swap back
// All in one tx!
```
**Detection**: Any `getReserves()`, `slot0()`, `balanceOf()` used for pricing is suspect.
### Root Cause 3: Staleness Blindness
Oracle price could be hours or days old, but protocol uses it anyway.
```solidity
// VULNERABLE: No staleness check
(, int256 price,,,) = chainlinkFeed.latestRoundData();
return uint256(price); // @audit Could be from yesterday!
// Attacker's opportunity:
// - Wait for oracle to become stale during volatility
// - Real price moved 20%, oracle still shows old price
// - Liquidate users at wrong price, or borrow too much
```
**Detection**: `latestRoundData()` without checking `updatedAt` timestamp.
### Root Cause 4: Composability Trust
Protocol blindly trusts external protocol's reported values.
```solidity
// VULNERABLE: Trusting external vault's totalAssets
function getCollateralValue(address user) view returns (uint256) {
uint256 shares = externalVault.balanceOf(user);
uint256 pricePerShare = externalVault.totalAssets() / externalVault.totalSupply();
return shares * pricePerShare; // @audit totalAssets can be donated to!
}
```
**Attacker's view**: "I can donate to that vault and inflate pricePerShare, then borrow against it."
---
## The Price Flow Map (Core Artifact)
Trace how prices flow through the system:
```
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Price Source │ ──► │ Reading Function │ ──► │ Critical Decision│
│ (Oracle/DEX) │ │ (getPrice, etc) │ │ (liquidate,mint)│
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │
▼ ▼
Manipulable? Value Transfer
- Flash loan? (funds move)
- Donation?
- Time window?
```
Document each price dependency:
| Function | Price Source | Manipulable? | Impact if Manipulated |
|----------|--------------|--------------|----------------------|
| liquidate() | Chainlink ETH/USD | Staleness only | Unfair liquidations |
| borrow() | Uniswap reserves | Flash loan | Undercollateralized borrow |
| mint() | vault.totalAssets() | Donation | Steal other users' deposits |
---
## Detection Patterns
### Pattern 1: Spot Price Dependency (Most Critical)
**Root Cause**: Single Source + Spot Price Trust
```solidity
// VULNERABLE: Direct reserve ratio
function getTokenPrice() public view returns (uint256) {
(uint112 r0, uint112 r1,) = pair.getReserves();
return uint256(r1) * 1e18 / uint256(r0); // @audit Instant manipulation
}
```
**Attack Flow**:
1. Flash loan large amount of token0
2. Swap to drain token0 from pool → price spikes
3. Call victim function that reads this price
4. Profit at inflated price
5. Swap back, repay flash loan
**Search Queries**:
```
Grep("getReserves|slot0|observe", glob="**/*.sol")
Grep("reserve0|reserve1|liquidity", glob="**/*.sol")
```
**Verification Questions**:
- Is this price used for critical decisions?
- Can flash loan move this price significantly?
- Is there enough liquidity to resist manipulation?
### Pattern 2: Oracle Staleness
**Root Cause**: Staleness Blindness
```solidity
// VULNERABLE: No freshness validation
function getPrice() external view returns (uint256) {
(, int256 price,,,) = priceFeed.latestRoundData();
return uint256(price); // @audit Could be stale!
}
// Attack opportunity:
// During high volatility, oracle updates lag
// Real ETH = $3000, Oracle still says $2500
// Attacker borrows at $2500 collateral value
// Immediately has undercollateralized position
```
**Search Queries**:
```
Grep("latestRoundData|latestAnswer", glob="**/*.sol")
Grep("updatedAt|answeredInRound", glob="**/*.sol")
``