June 20, 2025

Flash Loans: 5 Real Cases - How to Attack Them and How to Avoid Them

Introduction: Free Money With a Catch

Imagine someone offers you this deal:

“Borrow $1 million. Use it however you want. The only rule: give it back before the day ends. If you don’t, it never happened.”

Sounds insane, right?

That’s a flash loan.

It’s a blockchain primitive that shouldn’t exist by traditional finance logic, but does because of how blockchain executes code.

Here’s the magic: In the same transaction, you can:

  1. Borrow unlimited money
  2. Do whatever you want with it
  3. Repay it + fee
  4. If step 3 fails, the entire transaction reverts (the loan never happened)

The catch: It all happens in one atomic transaction. Usually measured in milliseconds.

This created an entirely new attack surface.

Protocols that seemed secure suddenly became vulnerable. Attackers found ways to exploit them.

But here’s what most people don’t understand: Flash loans aren’t inherently evil.

They’re a tool. They revealed gaps that needed fixing. They enable legitimate use cases (arbitrage, liquidations, risk assessment).

The problem is that most developers didn’t consider them during design.

This guide shows you 5 real attacks (or potential attacks) that flash loans enable, how they work conceptually, how to exploit them if you’re testing, and how to defend your protocol.


The Flash Loan Fundamental: Why They Work

Before we dive into attacks, you need to understand the core mechanism.

Why Flash Loans Are Possible

Traditional lending: Money leaves the bank, enters your account, you control it for days.

Flash loans: Money enters your contract, you execute code, money leaves your contract. All in one block.

The blockchain doesn’t care about intent. It only cares about:

  • Do all transactions succeed?
  • If yes, finalize everything
  • If any transaction fails, revert all

This atomicity is what makes flash loans possible.

NORMAL LENDING:
Day 1: You get money
Day 2-30: You use it
Day 31: You repay
(Bank has risk for 30 days)

FLASH LENDING:
Block N: You get money
Block N: You use it
Block N: You repay
(Lender has risk for microseconds)

Why They’re Dangerous

The danger comes from a fundamental insight: You can move unlimited capital in one block.

Normal attacks are limited by how much capital you personally have. Flash loans remove that limit.

Someone with $1,000 can now move $1,000,000.

If they’re clever, that million can be used to:

  • Manipulate prices
  • Trigger liquidations
  • Execute governance actions
  • All in the same block

Attack 1: Oracle Price Manipulation

The Vulnerability Class

This attack exploits protocols that use on-chain prices as truth.

Specifically: Protocols that query DEX prices to determine “current value” of an asset.

Why This Happens

Most lending protocols need to know: “What is ETH worth right now?”

They can’t ask an external API (too slow, centralized risk). So they look at Uniswap.

The logic seems sound: Uniswap is the most liquid market, so the price there should be accurate.

But what if you remove that liquidity? What if you artificially change the pool ratio?

The Attack Conceptually

Imagine a swimming pool that determines water value:

Normal: Pool has 1000L water and 1000L oil
        Ratio: 1L water = 1L oil

Attack: Remove 900L water by force
        Pool now has 100L water and 1000L oil
        Ratio: 1L water = 10L oil (fake!)

Protocol: "Wow, water is worth 10x oil now"
          (Makes decisions based on fake price)

Attacker: Profits from fake price, then fills pool back

How The Attack Actually Works

Phase 1: Preparation

Attacker sees: Uniswap has 1000 ETH and 2,000,000 USDC
             Current price: 1 ETH = 2000 USDC

Attacker thinks: "If I remove that ETH, price will spike"
                "Then I can borrow against fake collateral"
                "Then extract value"

Phase 2: Flash Loan Request

Attacker requests: 1 billion USDC flash loan

Lender accepts: "Give it back by end of block, plus 0.05% fee"

Phase 3: Manipulate The Pool

Attacker action: Spend 1 billion USDC to buy as much ETH as possible

Result: Uniswap pool goes from:
        1000 ETH / 2M USDC
        to
        100 ETH / 3M USDC

New price: 1 ETH = 30,000 USDC (from 2,000)
           Price spiked 15x

Phase 4: Exploit The Protocol

Lending protocol checks: "How much is Alice's collateral worth?"
                         Alice has 100 ETH
                         Current price (from Uniswap): 30,000/ETH
                         Value: 3,000,000 USDC

Lending protocol thinks: "Wow, she's very well collateralized"
                         "She can borrow lots"

Alice borrows: 2,000,000 USDC against 100 ETH

But: She only has 100 ETH because price spiked
     True value: 100 ETH * 2000 (real price) = 200,000
     She borrowed: 2,000,000
     She's now insolvent by 1.8M

Phase 5: Rebalance & Extract

Attacker sells back all ETH on Uniswap
Result: Uniswap pool returns to normal
        Price back to 2000 USDC/ETH

Attacker repays flash loan: 1B USDC + fee

Net result: 
Attacker has: 2M USDC (borrowed)
Attacker owes: 1B USDC (flash loan) + fee
Attacker loses money... UNLESS

The protocol can't liquidate fast enough
Or the attack triggers a cascade
Or the protocol's reserves get drained

Why This Is Dangerous (Real Examples)

This attack happened in various forms:

bZx Flash Loan Attack (2020):

  • Used flash loans to manipulate oracle
  • Profited $250,000+

Curve Finance (2023):

  • Complex oracle manipulation
  • Caused liquidations at bad prices
  • Lost $50M+

The Three-Layer Defense

Defense Layer 1: Multiple Price Sources

Don’t ask one pool. Ask three.

IF (prices from Uniswap, Curve, Balancer all agree)
    THEN use price
ELSE
    something weird happened, pause

If prices diverge suddenly, it’s likely manipulation.

Defense Layer 2: Time-Weighted Average Prices (TWAP)

Don’t use current price. Use average price over 1 hour.

Why this works: Flash loans execute in milliseconds. They can’t move the 1-hour average.

Current price: 2000 USDC/ETH (flash loan just spiked it)
TWAP (1 hour): 1990 USDC/ETH (the real price)

Protocol uses: 1990 (from TWAP)
Attacker manipulated: 2000 (current)

Attack fails because the protocol ignores the spike

Defense Layer 3: Circuit Breakers

If price moves >20% in one block, pause operations.

IF (current_price < last_price * 0.8 OR current_price > last_price * 1.2)
    THEN pause_critical_functions()

Rough, but effective. Stops the attack before it works.

What You Learn

Oracle manipulation teaches you: Never trust on-chain prices directly.

Prices are data. Data can be manipulated. Especially high-value data.

The solution: Layer defenses. Don’t bet the protocol on one oracle.


Attack 2: Liquidation Frontrunning

The Vulnerability Class

This attack exploits protocols where liquidations happen instantly and anyone can trigger them.

Why This Happens

In lending protocols, if you’re insolvent (borrowed too much relative to collateral), you get liquidated.

The protocol removes your collateral and sells it to pay your debt.

The liquidator (the person who triggered it) gets a reward (usually 5%).

The design assumption: Liquidators compete fairly to liquidate positions.

But what if one liquidator has unlimited capital (via flash loans)?

The Attack Conceptually

Imagine a bank where:

Normal: 10 insolvency opportunities per block
        10 liquidators race to liquidate
        Each gets one, earns 5% fee

Attack: 1 attacker with unlimited capital via flash loans
        Liquidates all 10 in the same block
        Earns 50% fee
        Other liquidators get nothing

But it’s more sinister than that.

How The Attack Actually Works

Phase 1: Monitor Network

Attacker watches: "Which positions are close to liquidation?"
                  Finds: Alice borrowed 1000 USDC against 1 ETH
                  At current price: 1 ETH = 1100 USDC (slightly safe)
                  But if price drops to 1000... liquidatable

Phase 2: Wait For The Right Moment

Price drops to: 1000 USDC per ETH
Alice's position: 1 ETH collateral = 1000 USDC value
                  Debt: 1000 USDC
                  Ratio: 1:1 (insolvent)
                  Liquidatable

Phase 3: Flash Loan To Trigger Liquidation

Attacker borrows: 1000 USDC (amount needed to repay Alice's debt)

Attacker calls: protocol.liquidate(Alice)
                Protocol requires: caller has debt repayment amount
                Attacker has it (just borrowed)

Phase 4: Extract The Reward

Protocol liquidates Alice:
                Takes: 1 ETH collateral
                Pays: 1000 USDC debt

Protocol gives: 1 ETH to attacker
                Plus: 50 USDC liquidation reward (5% fee)

Attacker now has: 1 ETH (worth ~1000)
                  50 USDC reward

Attacker repays: 1000 USDC flash loan + fee
                 50 USDC fee = 50.05 USDC

Attacker profit: Basically 0 (maybe -50 if fees align)

Wait, that doesn’t work. The attacker makes almost nothing.

The REAL attack:

What if multiple positions are liquidatable?

Attacker liquidates: 100 positions in one block
Each has liquidation reward: 50 USDC

Attacker earns: 100 * 50 = 5000 USDC
Attacker pays: 100 * 1000 + fees = 100,050
Attacker loss: Still negative

But WAIT. What if prices are wrong?

The ACTUAL dangerous attack:

Real Curve hack (2023):

Curve had a pricing oracle that was manipulable
Attacker manipulated: Price of stablecoin down (it should be $1)
Result: Curve thought stablecoins were worth less than $1

Curve liquidated positions based on WRONG price
Collateral was undervalued
Attacker could buy: $0.90 worth of collateral for $1

Flash loan helps with capital
But the real exploit was the oracle manipulation

The Defense

Defense 1: Liquidator Whitelist

Only specific liquidators can trigger liquidations.

mapping(address => bool) approvedLiquidators;

function liquidate(address user) external {
    require(approvedLiquidators[msg.sender]);
    // ... proceed
}

This prevents flash loan arbitrage because:

  • Limited set of liquidators
  • They won’t use flash loans (not needed, they have capital)
  • They earn small fee (not worth the overhead)

Defense 2: Liquidation Delay

Liquidations don’t execute immediately.

Phase 1: Mark for liquidation (tx 1)
         Takes: Some time (1 hour)

Phase 2: Execute liquidation (tx 2, must be after delay)
         Takes: Normal

Attack fails because:
Flash loans: Execute in 1 block
Delay: Requires 1+ blocks (or time)
Result: Attack impossible

Defense 3: Batch Liquidations

Instead of individual liquidations, batch them.

Protocol collects: All liquidatable positions
Calculates: Price ONCE for the batch
Liquidates: All at that price

Why this helps:
Attacker can't manipulate price individually
Price is set before liquidations execute
Even if prices move, all use same reference price

What You Learn

Liquidation attacks teach you: Instant critical actions are dangerous.

If something can happen in one block that affects protocol state permanently, attackers will exploit it.

The solution: Add delays, require approval, or batch operations.


Attack 3: Arbitrage Pool Draining

The Vulnerability Class

This attack exploits protocols with limited liquidity and direct arbitrage paths.

Why This Happens

DEXs use Automated Market Makers (AMMs):

  • Pool of Token A: 1000
  • Pool of Token B: 1000
  • Ratio maintains: Price is always determined by pool ratio

If you swap, you move the ratio, which moves the price.

Normal arbitragers balance pools. Prices across DEXs tend to converge.

But what if someone uses flash loans to create MASSIVE arbitrage opportunities that shouldn’t exist?

The Attack Conceptually

Normal arbitrage:
Uniswap price: 1 ETH = 2000 USDC
Pancake price: 1 ETH = 1950 USDC
Spread: 50 USDC (2.5%)

Arbitrager: Buy on Pancake, sell on Uniswap
            Earn: ~50 per ETH

Attack arbitrage:
Attacker: Borrow 1 BILLION USDC via flash loan
          Use it to drain Pancake pool
          Prices move 100x
          Drain is so extreme it breaks DEX mechanics

How The Attack Actually Works

Phase 1: Identify Opportunity

Two pools with limited liquidity:
Pancake: 100 ETH, 200k USDC
Uniswap: 100 ETH, 200k USDC

Price difference: ~2.5%

Attacker thinks: "If I remove liquidity from one side,
                  I can create massive price difference"

Phase 2: Flash Loan Large Amount

Attacker borrows: 2 million USDC

This is much larger than pools (100x)
This is key to the attack

Phase 3: Drain One Pool

Attacker swaps: 2M USDC → ETH on Pancake

Result: Pancake pool goes from:
        100 ETH / 200k USDC
        to
        ~2 ETH / 2.2M USDC

Price moved from: 1 ETH = 2000 USDC
        to: 1 ETH = 1,100,000 USDC (500x!)

Phase 4: Try To Profit

Attacker has: ~1,900 ETH (bought at insane prices)
              Paid: 2M USDC
              Cost per ETH: ~1,050 USDC

Attacker tries to: Sell on other DEX at normal price

Problem: Other DEXs also get drained
         Attacker sold to other pools
         Those pools also moved prices
         Attacker ends up paying gas for no profit

Phase 5: Return Loan

Attacker has: Still owes 2M USDC + fee
              Likely has less value than that
              Attack fails or barely breaks even

Why This Attack Rarely Works

This attack assumes:

  1. Multiple DEXs have massive price differences
  2. You can exploit all of them in one block
  3. You can turn the final position back to USDC profitably

In reality:

  • Liquidity is distributed (not concentrated in one pool)
  • Prices are too correlated (multiple oracles exist)
  • Slippage makes it unprofitable

But it has happened (Pancakeswap 2023), usually due to:

  • Extreme volatility
  • New tokens with low liquidity
  • Poor protocol design

The Defense

Defense 1: Minimum Liquidity Requirements

Protocols should require minimum liquidity before operating.

IF (pool_liquidity < MIN_LIQUIDITY)
    THEN don't_allow_swaps()

High minimum liquidity makes large attacks too expensive.

Defense 2: Price Impact Limits

Any single swap can’t move price more than X%.

price_before = get_price()
execute_swap()
price_after = get_price()

IF (price_after > price_before * 1.1)  // Price moved 10%+
    THEN revert()

Attacker can’t drain pools because swaps revert if they move price too much.

Defense 3: Slippage Parameters

Users specify minimum output price.

swap(USDC → ETH, min_output_price = 2000)

IF (actual_price < min_output_price)
    THEN revert()

Attacker’s massive swaps fail because outputs drop below minimums.

What You Learn

Arbitrage attacks teach you: Liquidity depth matters for security.

Shallow pools can be drained. Attacks work easier on illiquid tokens.

The solution: Require adequate liquidity, implement price limits, enforce slippage.


Attack 4: False Collateral Injection

The Vulnerability Class

This attack exploits protocols that accept arbitrary tokens as collateral without proper validation.

Why This Happens

Lending protocols want to be flexible: “Accept any ERC20 token as collateral.”

This seems generous. More options for users. More tokens can be deposits.

But it creates a dangerous pattern: What if someone deposits a token that’s:

  • Worthless
  • Manipulatable
  • Actually an attack vector

The Attack Conceptually

Normal collateral: ETH, stablecoin (liquid, clear value)

Malicious deposit: Custom ERC20 token
                   Price is what YOU say it is
                   You manipulate its price via flash loan
                   You borrow against fake collateral
                   You extract value

How The Attack Actually Works

Phase 1: Create or Find Manipulatable Token

Option A: Create your own token
          Token price depends on Uniswap pool
          Pool is illiquid
          Price is your to control

Option B: Find existing token
          Low liquidity
          Price easily moved

Phase 2: Flash Loan The Target Asset

Attacker borrows: 1M USDC (via flash loan)

This USDC gets: Swapped for the manipulatable token
                Price spikes 100x

Phase 3: Deposit Into Lending Protocol

Lending protocol sees: 1000 of custom token
                       Current price: 1 token = 1000 USDC (spiked)
                       Value: 1M USDC

Protocol believes: User has 1M USDC collateral!

Phase 4: Borrow Against Fake Collateral

User can now borrow: Up to 50M USDC (at 5x leverage)
                     Because they have "1M collateral"

User borrows: 40M USDC
              Against fake token worth 0

Phase 5: Crash The Token Price & Extract

Token price crashes: Back to 1 USDC per token (real value)
                     Collateral is now worth 1000 USDC
                     User is massively insolvent

User still has: 40M USDC borrowed
                1000 USDC actual collateral

Protocol tries to liquidate but:
Liquidity is low
Attacker already cashed out
Slips away with 40M

The Iron Bank / Cream Finance Real Examples

These attacks actually happened:

  • Attacker found low-liquidity tokens
  • Manipulated their prices
  • Deposited as collateral
  • Borrowed millions
  • Protocol lost tens of millions

The vulnerabilities varied but followed this pattern.

The Defense

Defense 1: Collateral Whitelist

Only approved tokens can be collateral.

mapping(address => bool) approvedCollateral;

function deposit(address token, uint256 amount) external {
    require(approvedCollateral[token]);
    // Only USDC, ETH, DAI, USDT, etc.
}

Simple but effective. Eliminates the attack.

Defense 2: Collateral Value Decay

If a token’s price spikes suddenly, don’t use the spike immediately.

IF (price_increased_more_than_20_percent)
    THEN use_yesterdays_price()
    ELSE use_current_price()

Flash loan spike: Ignored. Yesterday’s price still used.

Defense 3: Minimum Collateral Amounts

Users must deposit significant amounts.

require(collateral_value >= $10,000);

If you can’t deposit huge amounts of a token, the attack doesn’t scale.

What You Learn

Collateral attacks teach you: Not all assets are equal.

Illiquid, manipulatable assets shouldn’t be collateral.

The solution: Whitelist, decay prices, or require minimums.


Attack 5: Instant Governance Hijacking

The Vulnerability Class

This attack exploits protocols where governance voting happens instantly without delays or safeguards.

Why This Happens

DAOs want to be decentralized. More people voting is better.

Some DAOs optimize for speed: Decisions execute fast. Community-driven governance in real-time.

But instant governance has a problem: What if someone votes with borrowed tokens?

The Attack Conceptually

Normal voting: Your voting power = your tokens
               You own them for weeks/months
               You commit when you vote

Malicious voting: Your voting power = tokens you borrowed 1 second ago
                  You return them 1 second after voting
                  You had no real stake
                  You just hijacked governance

How The Attack Actually Works

Phase 1: Identify Governance Action

DAO discusses: "Should we change Treasury allocation?"
               Proposal being voted on
               Results not finalized yet

Attacker sees: Potential to hijack this decision

Phase 2: Flash Loan Governance Tokens

Attacker borrows: 10M governance tokens

These tokens: Represent 80% of total circulating
              Enough to pass almost any proposal alone

Phase 3: Vote

Attacker calls: governance.vote(proposalId, YES)
                Voting power: 10M tokens
                Vote weight: 80%

Proposal state changes: From "tied" to "PASSED"
                        Pending the execution

Phase 4: Malicious Proposal Executes

Proposal: "Transfer all Treasury to address 0x123"
          (Attacker's address)

Execution happens immediately
All DAO funds: Transferred to attacker

Attacker sees funds arrive

Phase 5: Return Flash Loan

Same block: Attacker repays flash loan + fee
            Flash loan lender: Gets their tokens back

To outside observer: Tokens never actually left
                     But governance was hijacked
                     And funds were stolen

This is pure chaos.

Real Examples

This hasn’t caused massive hacks yet (most DAOs implement safeguards).

But theoretically possible if:

  • Governance votes instantly without delay
  • No voting escrow (ve-tokenomics)
  • Proposals execute immediately after voting

The Defense

Defense 1: Voting Escrow (ve-Tokenomics)

You can’t vote with tokens you just acquired.

function lock_tokens(uint256 amount, uint256 lock_duration) {
    require(lock_duration >= 4 weeks);
    // Mint voting power
    voting_power[msg.sender] = amount;
}

function vote(uint256 proposal_id) {
    require(voting_power[msg.sender] > 0);
    // Flash loans can't lock for 4 weeks (same block)
}

Used by: Curve, Balancer, Uniswap, most modern DAOs

Defense 2: Voting Snapshot

Voting power is based on historical balance, not current.

function start_voting() {
    snapshot_block = current_block - 1
    // Use yesterday's balance
}

function vote(uint256 proposal_id) {
    voting_power = token.balance_at_block(msg.sender, snapshot_block)
    // Flash loans are new, not in snapshot
}

Flash loans can’t affect historical data.

Defense 3: Execution Delay

Proposals don’t execute immediately after passing.

Proposal passes: In block 1000
Execution: Only allowed after block 1010

Why: Gives community time to respond
     If attack detected, can pause
     Flash loans execute in 1 block

What You Learn

Governance attacks teach you: Instant critical actions are extremely dangerous.

Governance controls protocol parameters. If someone can control it instantly with borrowed assets, it’s game over.

The solution: Lock tokens, use snapshots, add delays.


The Synthesis: Why These All Matter

Flash loan attacks follow a pattern:

PATTERN:
1. Identify something worth manipulating (price, liquidation, governance, etc)
2. Use flash loan to get capital for the manipulation
3. Execute the attack in one atomic block
4. Repay the flash loan (making it look clean)
5. Extract value

If your protocol has:

  • On-chain price dependency
  • Instant critical actions
  • No capital requirements
  • Permissionless execution

Then you’re vulnerable.


For Developers: The Checklist

Before deploying, ask:

ORACLE QUESTIONS:
□ Do I depend on current on-chain prices?
   If yes: Do I have TWAP, multiple sources, or circuit breakers?
□ What happens if price moves 100x in one block?
   If protocol breaks: Fix it

LIQUIDATION QUESTIONS:
□ Can anyone liquidate anyone?
   If yes: Do they need capital? Whitelist?
□ Can liquidations happen in one block?
   If yes: Is that intentional?

GOVERNANCE QUESTIONS:
□ Can voting happen instantly?
   If yes: Do voters need to lock tokens first?
□ Can proposals execute immediately after passing?
   If yes: Is there a delay mechanism?

GENERAL QUESTIONS:
□ Can someone move unlimited capital in one block?
   If yes: How does my protocol respond?
□ Are there critical state changes that happen instantly?
   If yes: Can they be attacked via flash loans?

If you answer “yes” to 3+ questions, audit for flash loan vectors.


The Uncomfortable Truth

Flash loans exposed a design pattern that was invisible: Protocols assumed capital constraints.

For 50 years, finance assumed: “If you don’t have capital, you can’t attack the system.”

Flash loans broke that assumption.

Now defenders must assume: “An attacker has unlimited capital for one block.”

This changed how we design protocols fundamentally.


Conclusion: The Future Is Here

Flash loans are not going away. They’re becoming more common.

The protocols that survive:

  • Build with flash loans in mind
  • Assume unlimited one-block capital
  • Add delays to critical actions
  • Use multiple price sources
  • Implement circuit breakers

The protocols that fail:

  • Ignore flash loan vectors
  • Assume instant actions are fine
  • Trust single oracles
  • Assume capital constraints

You now know which one you want to be.

Build defensively.