Flash Loans: Borrowing Billions to Break DeFi
9 min read
June 28, 2026

Table of contents
👋 Introduction
Hey everyone!
Last week we covered eBPF rootkits giving attackers kernel-level credential harvesting and network invisibility. This week we switch domains entirely: back to Web3, where issues have focused on infra and auth for a while. It’s overdue.
Flash loans are the most counterintuitive primitive in decentralized finance. A protocol lends you 500 million with no collateral, no credit check, one rule: repay in the same transaction. If you can’t, the entire transaction reverts as if it never happened. For lenders, the risk is zero. For attackers, the $500 million is effectively free capital for the duration of one Ethereum block.
The result: attacks that require hundreds of millions of dollars to execute normally become available to anyone who can write a contract. Price oracle manipulation, governance hijacking, and liquidity draining collapse into a single transaction. This week: how flash loans work, what they break, and the best-documented $197M exploit on record.
Let’s get into it 👇
💸 The Flash Loan Mechanism
Flash loans exist because of Ethereum’s atomicity guarantee: everything in a transaction either commits entirely or reverts entirely. A lending protocol releases $500M knowing that if you don’t repay, the release never happened. No counterparty risk. No collateral. Just gas.
Aave V3 implements the canonical interface. Your contract must implement IFlashLoanSimpleReceiver and override executeOperation():
// Your contract receives the borrowed funds mid-transaction
function executeOperation(
address asset,
uint256 amount,
uint256 premium, // Aave's 0.05% fee
address initiator,
bytes calldata params
) external override returns (bool) {
// All attack logic runs here:
// manipulate prices, drain pools, vote on proposals...
// Approve repayment before returning, must include fee
IERC20(asset).approve(address(POOL), amount + premium);
return true;
}
Uniswap V3 flash swaps work via a pool-level callback: the protocol sends tokens first, then calls your uniswapV3FlashCallback. You execute within the callback and repay before it returns. Fees range from 0.05% to 1% depending on pool tier.
The critical insight: these fees are tiny compared to potential exploit profit. Borrowing 50M to extract 20M makes sense if you repay the 50M plus 25K in fees and pocket the rest. Net cost: gas.
📉 Price Oracle Manipulation: Spot vs. TWAP
The first major attack class targets price oracles. DeFi lending protocols need to know the value of collateral tokens. The naive implementation reads the current reserve ratio from a Uniswap V2 pool via getReserves().
In a constant-product AMM (x * y = k), depositing a large amount of one token moves the price. Flash borrow $50M in ETH, dump it into an ETH/USDC pool, and the pool now reports USDC as dramatically more valuable per ETH within that block. Any protocol reading getReserves() sees a manipulated price.
// Vulnerable oracle pattern, do not use in production
function getTokenPrice(address pair) external view returns (uint256) {
(uint112 reserve0, uint112 reserve1,) =
IUniswapV2Pair(pair).getReserves();
// A flash loan in the same block can move this spot price
return (uint256(reserve1) * 1e18) / reserve0;
}
The bZx attack #2 in February 2020 demonstrated this at $665K profit: flash-borrowed 7,500 ETH, dumped 900 ETH through Kyber to inflate the sUSD price 2.5x, then borrowed 6,796 ETH from bZx using the inflated sUSD collateral. One transaction. No capital required upfront.
TWAP (Time-Weighted Average Price) oracles resist this because they average price across many blocks over a time window. A flash loan manipulates one block’s spot price. Moving a 30-minute TWAP requires sustained pressure across hundreds of blocks, making it economically impractical for most attackers. Any protocol using getReserves() for anything security-sensitive is vulnerable to flash loans by design.
🗳️ Governance Hijacking: Borrow Tokens, Control Protocols
DeFi protocols often govern themselves via token-weighted voting: hold governance tokens, vote on proposals. Flash loans break this model when protocols allow voting and execution in the same transaction.
Beanstalk Protocol lost 182M on April 17, 2022. The attacker flash-borrowed over $1 billion in stablecoins, converted to Curve LP tokens, and deposited into Beanstalk to acquire Stalk (voting power). Beanstalk’s emergencyCommit() function executed a proposal immediately if it reached a two-thirds supermajority:
// The vulnerable pattern: vote and execute atomically
function emergencyCommit(uint32 bip) external {
require(
balanceOfStalk(msg.sender) >= totalStalk() * 2 / 3,
"Not enough Stalk"
);
// No timelock, no block delay
_execute(bip);
}
The malicious proposal drained all LP positions to the attacker. Repay the flash loan. Keep ~$76M. Total time: one transaction. The ERC-20 Votes extension (snapshot-based voting) defeats this by recording voting power at a past block number, making same-transaction vote acquisition impossible.
ERC-20 token mechanics, which we covered in Issue 7, are what the flash loan contract exploits through approve/transferFrom when moving borrowed capital between protocols in a single transaction.
🥪 MEV Sandwich Attacks
MEV (Maximal Extractable Value) describes value captured by controlling transaction ordering within a block. The sandwich attack is the most common form targeting regular DEX users.
A searcher’s bot monitors the public mempool for large pending swaps. When it detects a swap likely to move the pool price significantly, the bot submits two transactions bracketing the victim’s:
- Front-run: the bot buys Token X just before the victim, pushing the price up
- Victim transaction executes at a worse rate (the bot’s buy already shifted the pool)
- Back-run: the bot sells Token X immediately after, pocketing the victim’s slippage
// Flashbots bundle: atomic front-run + back-run without mempool exposure
const bundle = [
{ transaction: frontRunTx, signer: attackerWallet },
{ transaction: victimTx },
{ transaction: backRunTx, signer: attackerWallet }
];
const flashbotsProvider = await FlashbotsBundleProvider.create(
provider, signingWallet
);
// eth_callBundle simulates profitability before committing gas
const simResult = await flashbotsProvider.simulate(bundle, targetBlock);
await flashbotsProvider.sendBundle(bundle, targetBlock);
Flashbots enables eth_sendBundle submissions directly to block builders, bypassing the public mempool entirely. Front-run and back-run execute atomically in the same block with no risk of partial execution. The practical defense for users is a tight amountOutMin in swap calls and private transaction submission via eth_sendPrivateTransaction to avoid mempool exposure.
💀 Case Study: Euler Finance ($197M, March 2023)
Euler Finance’s March 2023 exploit is the most documented flash loan attack on record. The Chainalysis post-mortem traces every transaction.
The vulnerability was in Euler’s donateToReserves() function. This auxiliary function burned eTokens (collateral receipt tokens) but did not burn the corresponding dTokens (debt tokens). Calling it created an artificially insolvent position: reduced collateral, unchanged debt. Euler’s own liquidation mechanism then became the attack vector:
// Simplified attack loop (repeated across DAI, wBTC, stETH, USDC pools):
// 1. Flash borrow ~$30M DAI from Aave
// 2. deposit(20M DAI) -> receive eDAI (collateral tokens)
// 3. mint(200M) -> Euler's leverage borrows 10x against the eDAI
// 4. repay(10M dDAI) -> reduces debt slightly
// 5. donateToReserves(10M eDAI) -> burns collateral, DOES NOT burn debt
// Position is now technically insolvent (less collateral, same debt)
// 6. softLiquidate(self) -> Euler's liquidation seizes OTHER users'
// collateral at a discount to cover the manufactured insolvency
// 7. Repeat across pools, repay Aave flash loan
The donateToReserves() function bypassed Euler’s health check. It was added to support reserve donations and was never recognized as an attack surface. Sherlock, the audit platform covering Euler, paid a $4.5M claim for missing it. The attacker ultimately returned all funds after on-chain negotiation, which is why the documentation is this complete.
🛠️ Tools and Practice
Foundry is the standard toolkit for flash loan exploit development. forge test --fork-url <mainnet-rpc> runs transactions against live mainnet state in a local sandbox. Anvil provides the fork. Cast handles contract interaction.
DeFiHackLabs contains Foundry reproductions of hundreds of real DeFi exploits including every major flash loan attack:
git clone https://github.com/SunWeb3Sec/DeFiHackLabs
cd DeFiHackLabs
forge test --match-contract Euler_exp -vvvv
# Replays the full $197M Euler exploit against a mainnet fork
Flashbots’ ethers bundle provider provides the eth_callBundle simulation method for MEV research. Test sandwich profitability before committing real gas.
Damn Vulnerable DeFi has eight flash-loan-relevant challenges. “Selfie” (Challenge 6) requires flash-borrowing governance tokens to take over a DAO and mirrors the Beanstalk mechanic exactly. “Puppet V2” (Challenge 9) requires manipulating a Uniswap V2 oracle to drain a lending pool. Both map directly to the attack patterns in this issue.
🎯 Key Takeaways
Flash loans don’t introduce new vulnerabilities. They remove the capital barrier to exploiting existing ones. A price oracle that resists an attacker with 10K becomes exploitable to anyone with gas money when $500M is available on demand. Every DeFi protocol reading a spot price from an AMM has flash loan exposure by design. The solution isn’t preventing flash loans. It’s not reading spot prices.
The oracle hierarchy: getReserves() spot price is trivially manipulable in one block, TWAP oracles resist single-block attacks but require sufficiently long windows, and off-chain feeds like Chainlink are flash-loan resistant by construction. Any protocol using on-chain spot prices for collateral valuation, liquidation thresholds, or anything that moves funds should be treated as vulnerable regardless of other controls.
Governance that allows voting and execution in the same transaction is structurally broken against flash loans. The minimum viable fix is snapshot voting at a past block. The right fix adds a multi-day timelock between vote and execution. If you see an emergencyCommit or equivalent immediate-execution function in a governance contract during an audit, flag it as critical regardless of other protections.
For practice: run DeFiHackLabs’ Euler reproduction first. The full $197M exploit plays out in your terminal in under 30 seconds. Read the test code backward from the profit calculation to understand which state transitions enabled it. That pattern, reading real exploit code against real forked state, teaches more than any theoretical walkthrough.
Practice:
- Damn Vulnerable DeFi - 12 challenges, 8 flash-loan relevant (Selfie and Puppet V2 first)
- DeFiHackLabs (SunWeb3Sec) - Foundry reproductions of Euler, Cream, bZx, and 200+ more
- Foundry (foundry-rs) - Forge/Cast/Anvil for writing and running exploit PoCs against mainnet forks
- Aave V3 Flash Loan Docs - canonical flash loan interface reference
- Flashbots MEV Documentation - eth_sendBundle, MEV-Boost, and Flashbots infrastructure
- Flashbots ethers bundle provider - JavaScript library for MEV bundle simulation and submission
- Euler Finance exploit analysis (Chainalysis) - best post-mortem of the $197M attack with full transaction trace
- Cream Finance hack analysis (Immunefi) - $130M flash loan plus oracle plus deposit looping attack
Thanks for reading, and happy hunting!
— Ruben
Other Issues
Previous Issue
💬 Comments Available
Drop your thoughts in the comments below! Found a bug or have feedback? Let me know.