Securite·9 min de lecture·Par Solingo

Flash Loan Attacks Explained — How Millions Get Stolen

Explore how flash loan attacks exploit DeFi protocols through oracle manipulation and governance attacks. Learn prevention strategies with TWAP oracles and Chainlink.

# Flash Loan Attacks Explained — How Millions Get Stolen

Flash loans are one of DeFi's most innovative features — and one of its most exploited attack vectors. In 2021 alone, over $300 million was stolen through flash loan attacks.

Despite being a legitimate DeFi primitive, flash loans amplify existing vulnerabilities, allowing attackers to manipulate markets, oracles, and governance with zero capital upfront.

In this article, we'll dissect how flash loan attacks work, examine real-world exploits, and learn proven defense strategies.

What Are Flash Loans?

A flash loan is an uncollateralized loan that must be borrowed and repaid within a single transaction.

Key Characteristics

  • No collateral required — anyone can borrow millions instantly
  • Atomic transaction — loan + operations + repayment happen in one block
  • Instant liquidity — access to massive capital without owning it
  • Zero risk for lender — if not repaid, entire transaction reverts

Example (Aave Flash Loan)

interface IFlashLoanReceiver {

function executeOperation(

address[] calldata assets,

uint256[] calldata amounts,

uint256[] calldata premiums,

address initiator,

bytes calldata params

) external returns (bool);

}

contract FlashLoanUser is IFlashLoanReceiver {

ILendingPool public lendingPool;

function executeFlashLoan(address asset, uint256 amount) external {

address[] memory assets = new address[](1);

assets[0] = asset;

uint256[] memory amounts = new uint256[](1);

amounts[0] = amount;

uint256[] memory modes = new uint256[](1);

modes[0] = 0; // No debt

lendingPool.flashLoan(

address(this),

assets,

amounts,

modes,

address(this),

"",

0

);

}

function executeOperation(

address[] calldata assets,

uint256[] calldata amounts,

uint256[] calldata premiums,

address initiator,

bytes calldata params

) external override returns (bool) {

// You now have amounts[0] of assets[0]

// Do something with the flash loaned funds

// ... your logic here ...

// Repay loan + fee

uint256 amountOwed = amounts[0] + premiums[0];

IERC20(assets[0]).approve(address(lendingPool), amountOwed);

return true;

}

}

Why Flash Loans Are Dangerous

Flash loans themselves aren't malicious, but they amplify existing vulnerabilities by providing:

  • Unlimited capital — exploit small inefficiencies with massive scale
  • Atomic execution — complex multi-protocol attacks in one transaction
  • Zero risk — if attack fails, transaction reverts (no loss to attacker)
  • Flash Loan Attack Vectors

    1. Oracle Manipulation

    Most common attack type. Manipulate price oracles to exploit protocols that rely on them.

    Vulnerable Oracle Pattern

    contract VulnerableLending {
    

    IERC20 public tokenA;

    IUniswapV2Pair public pair; // TokenA/WETH

    // VULNERABLE: Using spot price from DEX

    function getPrice() public view returns (uint256) {

    (uint112 reserve0, uint112 reserve1,) = pair.getReserves();

    return (reserve1 * 1e18) / reserve0; // Instant price

    }

    function borrow(uint256 amount) external {

    uint256 collateralRequired = (amount * getPrice()) / 1e18;

    require(tokenA.balanceOf(msg.sender) >= collateralRequired);

    // User can borrow based on manipulated price

    // ...

    }

    }

    Attack Scenario

    contract OracleAttack {
    

    function attack() external {

    // 1. Flash loan large amount of TokenA

    uint256 loanAmount = 1000000 * 1e18;

    flashLoan(address(tokenA), loanAmount);

    }

    function executeOperation(/*...*/) external {

    // 2. Dump TokenA on DEX, crashing price

    tokenA.approve(address(router), loanAmount);

    router.swapExactTokensForTokens(

    loanAmount,

    0,

    path,

    address(this),

    block.timestamp

    );

    // Price is now artificially low

    // 3. Borrow massive amount from lending protocol

    // (which thinks collateral is worth more due to low TokenA price)

    lendingProtocol.borrow(exploitAmount);

    // 4. Swap back to restore price

    router.swapExactTokensForTokens(/*...*/);

    // 5. Repay flash loan

    tokenA.transfer(lender, loanAmount + fee);

    // Attacker keeps the over-borrowed funds

    }

    }

    Real example: Harvest Finance (2020) — $24M stolen via price manipulation on curve pools.

    2. Governance Attacks

    Flash loan massive governance tokens to pass malicious proposals.

    Vulnerable Governance

    contract VulnerableDAO {
    

    IERC20 public govToken;

    struct Proposal {

    address target;

    bytes data;

    uint256 forVotes;

    uint256 againstVotes;

    bool executed;

    }

    mapping(uint256 => Proposal) public proposals;

    // VULNERABLE: Snapshot at vote time, not proposal creation

    function vote(uint256 proposalId, bool support) external {

    uint256 votes = govToken.balanceOf(msg.sender);

    if (support) {

    proposals[proposalId].forVotes += votes;

    } else {

    proposals[proposalId].againstVotes += votes;

    }

    }

    function execute(uint256 proposalId) external {

    Proposal storage prop = proposals[proposalId];

    require(prop.forVotes > prop.againstVotes);

    require(!prop.executed);

    prop.executed = true;

    (bool success,) = prop.target.call(prop.data);

    require(success);

    }

    }

    Attack

    function governanceAttack() external {
    

    // 1. Flash loan governance tokens

    flashLoan(address(govToken), 51% of supply);

    // 2. Create malicious proposal

    dao.propose(address(treasury), abi.encodeWithSignature(

    "transfer(address,uint256)",

    attacker,

    treasury.balance

    ));

    // 3. Vote with flash loaned tokens

    dao.vote(proposalId, true);

    // 4. Execute immediately (if no timelock)

    dao.execute(proposalId);

    // 5. Repay flash loan

    govToken.transfer(lender, amount + fee);

    }

    Real example: Beanstalk (2022) — $182M stolen via governance takeover with flash loaned tokens.

    3. Reentrancy Amplification

    Flash loans provide capital for reentrancy attacks on poorly protected protocols.

    function amplifiedReentrancy() external {
    

    // 1. Flash loan large amount

    flashLoan(10000 ether);

    // 2. Deposit to vulnerable protocol

    vulnerable.deposit{value: 10000 ether}();

    // 3. Exploit reentrancy to withdraw repeatedly

    vulnerable.withdraw(10000 ether);

    // Reentrancy drains more than deposited

    // 4. Repay flash loan

    lender.transfer(10000 ether + fee);

    // Keep the excess

    }

    4. Liquidation Manipulation

    Manipulate collateral prices to trigger mass liquidations, then buy the collateral cheap.

    Real-World Flash Loan Attacks

    Harvest Finance (October 2020)

    • Stolen: $24M
    • Method: Curve pool price manipulation
    • Flash loan from: Uniswap (no fee at the time)
    • Result: Protocol survived but users lost funds

    PancakeBunny (May 2021)

    • Stolen: $45M
    • Method: Price oracle manipulation on PancakeSwap
    • Impact: BUNNY token crashed 96%

    Cream Finance (August 2021)

    • Stolen: $18.8M
    • Method: Flash loan + price oracle manipulation on AMP token
    • Repeat: Cream was hacked 3 times in 2021

    Beanstalk (April 2022)

    • Stolen: $182M
    • Method: Flash loan governance attack
    • Loans from: Aave ($1B borrowed)
    • Execution: Passed malicious proposal, drained treasury

    Euler Finance (March 2023)

    • Stolen: $197M (later returned)
    • Method: Flash loan + donation attack to manipulate debt tracking
    • Largest DeFi hack of 2023

    Prevention Strategies

    1. TWAP Oracles (Time-Weighted Average Price)

    Don't use spot prices. Use time-weighted averages that can't be manipulated in a single transaction.

    contract TWAPOracle {
    

    IUniswapV2Pair public pair;

    uint256 public priceAverage;

    uint256 public lastUpdate;

    uint32 public constant PERIOD = 10 minutes;

    function update() external {

    uint32 timeElapsed = uint32(block.timestamp - lastUpdate);

    require(timeElapsed >= PERIOD, "Too soon");

    (uint256 price0Cumulative, uint256 price1Cumulative,) =

    UniswapV2OracleLibrary.currentCumulativePrices(address(pair));

    priceAverage = (price0Cumulative - price0CumulativeOld) / timeElapsed;

    lastUpdate = block.timestamp;

    price0CumulativeOld = price0Cumulative;

    }

    function getPrice() external view returns (uint256) {

    return priceAverage; // Manipulation-resistant

    }

    }

    Why it works: Flash loan attacks happen in one transaction. TWAP requires price changes over many blocks.

    Use decentralized, external price oracles that can't be manipulated via on-chain trades.

    import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol";
    
    

    contract SecureLending {

    AggregatorV3Interface internal priceFeed;

    constructor(address _priceFeed) {

    priceFeed = AggregatorV3Interface(_priceFeed);

    }

    function getLatestPrice() public view returns (int) {

    (

    uint80 roundID,

    int price,

    uint startedAt,

    uint timeStamp,

    uint80 answeredInRound

    ) = priceFeed.latestRoundData();

    require(timeStamp > 0, "Round not complete");

    return price;

    }

    }

    Benefits:

    • Data aggregated from multiple sources
    • Updated off-chain (not manipulable via flash loans)
    • Battle-tested across billions in TVL

    3. Snapshot Governance

    For DAOs, use block-based snapshots taken before voting starts.

    contract SecureGovernance {
    

    mapping(uint256 => uint256) public proposalSnapshots;

    function propose(/*...*/) external returns (uint256) {

    uint256 proposalId = proposalCount++;

    // Snapshot BEFORE voting starts

    proposalSnapshots[proposalId] = block.number;

    return proposalId;

    }

    function vote(uint256 proposalId, bool support) external {

    // Use balance at snapshot block

    uint256 votes = govToken.balanceOfAt(

    msg.sender,

    proposalSnapshots[proposalId]

    );

    // Flash loans after proposal creation don't count

    }

    }

    4. Execution Delays

    Add timelocks to prevent instant execution.

    contract TimelockDAO {
    

    uint256 public constant DELAY = 2 days;

    struct Proposal {

    uint256 eta; // Earliest execution time

    // ...

    }

    function queue(uint256 proposalId) external {

    require(proposalPassed(proposalId));

    proposals[proposalId].eta = block.timestamp + DELAY;

    }

    function execute(uint256 proposalId) external {

    require(block.timestamp >= proposals[proposalId].eta);

    // Execute

    }

    }

    Flash loans must be repaid in one transaction, so they can't wait 2 days.

    5. Flash Loan Detection

    Block flash loan usage for sensitive functions.

    contract FlashLoanProtected {
    

    mapping(address => uint256) private balanceSnapshot;

    modifier noFlashLoan() {

    uint256 balanceBefore = balanceSnapshot[msg.sender];

    // First interaction in this transaction

    if (balanceBefore == 0) {

    balanceSnapshot[msg.sender] = token.balanceOf(msg.sender);

    } else {

    // Balance shouldn't spike in same transaction

    require(

    token.balanceOf(msg.sender) <= balanceBefore * 2,

    "Flash loan detected"

    );

    }

    _;

    }

    function sensitiveAction() external noFlashLoan {

    // Protected

    }

    }

    Note: This can have false positives, use with caution.

    Best Practices Summary

    For Lending Protocols

    ✅ Use Chainlink or TWAP oracles, never spot prices

    ✅ Implement sanity checks on price changes

    ✅ Add circuit breakers for abnormal market conditions

    ✅ Require multi-block delays for large position changes

    For DAOs

    ✅ Snapshot balances at proposal creation

    ✅ Add timelocks (minimum 24-48 hours)

    ✅ Require quorum from long-term holders

    ✅ Use vote delegation to prevent last-minute governance attacks

    For All DeFi Protocols

    ✅ Audit all price dependencies

    ✅ Test with flash loan attack scenarios

    ✅ Monitor for unusual transaction patterns

    ✅ Have emergency pause mechanisms

    ✅ Maintain bug bounty programs

    Conclusion

    Flash loans are a powerful DeFi primitive that democratizes access to capital — but they also democratize access to exploits.

    The attacks aren't caused by flash loans themselves, but by underlying vulnerabilities that flash loans amplify:

    • Weak oracles (use TWAP or Chainlink)
    • Instant governance (add snapshots and timelocks)
    • Reentrancy (use guards)
    • Unprotected liquidations (add delays)

    The bottom line: If your protocol can be exploited with enough capital, assume someone will flash loan that capital.

    Practice flash loan defense on Solingo — our exercises simulate real attacks including oracle manipulation and governance takeovers, with step-by-step defense implementation.

    Prêt à mettre en pratique ?

    Applique ces concepts avec des exercices interactifs sur Solingo.

    Commencer gratuitement