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:
- Borrow unlimited money
- Do whatever you want with it
- Repay it + fee
- 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:
- Multiple DEXs have massive price differences
- You can exploit all of them in one block
- 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.