Skip to main content
ClaudeWave
Skill55 repo starsupdated 4mo ago

economic-attack

>

Install in Claude Code
Copy
git clone --depth 1 https://github.com/PurpleAILAB/Vigilo /tmp/economic-attack && cp -r /tmp/economic-attack/packages/opencode/skills/economic-attack ~/.claude/skills/economic-attack
Then start a new Claude Code session; the skill loads automatically.

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")
``