Flash Loan Attacks: A Case Study

Flash Loan Attacks: A Case Study
Cyber Labs

12 of 12

This insight is part 12 of 12 in this Collection.

October 13, 2023 10 mins

Flash Loan Attacks: A Case Study

Flash Loan Attacks: A Case Study Hero Banner

This blog post explains how flash loans work, their history, and their role in smart contract attacks. We then explore the first major flash loan attack, discuss current threats, and offer security best practices to prevent them.

A flash loan is a relatively new type of loan that is taken on a blockchain network such as Ethereum. In this blog post, we are going to introduce how flash loans work, their history, and how they are incorporated into attacks on smart contracts (“flash loan attacks”). We will then present a case study of the first ever major flash loan attack, discuss what these attacks look like today, and provide a brief overview of some security best practices that can help to prevent them.

A flash loan is a novel financial product that doesn’t use collateral up front but charges a transaction fee when the borrowed assets are returned. A loan without collateral may not make a lot of sense in a traditional financial system, but due to the atomic nature of blockchain transactions these loans offer little risk to lenders. If the loan isn’t repaid by the end of a transaction, the entire transaction is simply reverted. This is possible due to the validation and consensus process that the blockchain network provides.

Flash lending made its debut in 2018 by the Marble Protocol and quickly found popularity with traders looking to profit off arbitrage opportunities between decentralized exchanges. It rose in popularity in 2020, is now offered by many DeFi platforms supporting a wide range of different assets and has been standardized as EIP-3156.

Unfortunately, access to extremely cheap liquidity isn’t only used by good-faith actors. The invention of flash loans also gave rise to flash loan “attacks”. Flash loan attacks make use of flash loans during the exploitation of smart contract bugs or the manipulation of cryptocurrency asset prices on an exchange (e.g., “wash trading”). They are a bit of a misnomer since they aren’t really attacks by themselves. Rather, flash loan attacks are used to augment the impact of another form of malicious behavior and maximize profit for an attacker.

The First Flash Loan Attack

The first major flash loan attack occurred in 2020 against the bZx lending pool. bZx had just announced Fulcrum the year prior, a new tokenized loan and margin platform powered by their existing protocol. What ensued was the exploitation of a vulnerability in one of bZx’s smart contracts that allowed an attacker to open a large under-collateralized short position on Fulcrum with a flash loan. The position was short ETH in favor of wrapped Bitcoin (wBTC).

The contract should have calculated the true value of the position’s collateral by taking into consideration price slippage and checking for a potential liquidation event, but due to a logic bug (that we will explore later) it never did. Instead, the attacker’s actions forced bZx to purchase 112 wBTC on the Uniswap decentralized exchange, which triggered such large slippage that the order ended up costing roughly 3x the market price. This created an artificial arbitrage opportunity that the attacker subsequently exploited within the same transaction. As a result, the attacker ended up making a profit of $370,000 while bZx suffered a loss of $620,000.

On-Chain Analysis and Code Review

While we don’t have the source code of the attacker’s exploit contract, we can examine the resulting transaction and walk through it with a debugger to better understand what happened, such as the one integrated with tenderly or phalcon. Doing this, we can see that the attacker takes a flash loan of 10,000 ETH from dYdX, which transfers the equivalent wrapped ETH (wETH) to the attacker’s contract (0x4f4e0f2cb72e718fc0433222768c57e823162152) and calls into it. The attacker’s contract then makes the following series of calls:

  • widthdraw: Withdraws the 10,000 wETH as ETH from the flash loan.
  • enterMarkets: Enters into a list of markets on Compound v2 (cWBTC and cETH).
  • mint: Sends 5,500 ETH to Compound and mints 5,500 cTokens.
  • balanceOfUnderlying: Checks the attacker’s underlying balance on Compound.
  • borrow: Uses the 5,500 cTokens to borrow 112 wBTC.
  • approve: Approves the borrowed 112 wBTC to be transferred to Uniswap (which happens later).
  • mintWithEther: This is where bZx’s vulnerable code gets triggered.
    • Calls the Fulcrum Perpetual Short ETH-WBTC 5x v2 contract (0xb0200b0677dd825bb32b93d055ebb9dc3521db9d) to open a short position of 1,300 ETH in favor of wBTC with 5x leverage.
    • Uses Kyber to discover a price for the shorted pair and an exchange that can fulfill the order. Uniswap is selected.
    • The resulting Uniswap trade triggers slippage and bZx is quoted at a conversion rate of 0.0091076627270987. This is a terrible rate compared to the average market rate at the time (though at no fault of Uniswap), and it drives the price of wBTC up on Uniswap during this transaction allowing the attacker to later sell at about 3x the market rate.
    • The slippage is so large that overcollateralization doesn’t cover it. This will trigger liquidation for bZx, which as previously mentioned should have been caught by their own contract. The order is executed anyway.
  • balanceOf: Checks the attacker’s balance of wBTC (which is the previously borrowed 112 wBTC).
  • delegatecall to Uniswap: The attacker sells their previously approved 112 wBTC for 6,871 wETH (about 2x the market rate).
  • deposit: Deposits the ETH owed to dYdX as wETH.
  • transfer: Transfers the deposited wETH to the attacker’s account.
  • transferFrom: Transfers the wETH owed to dYdX.

At this point the attacker’s exploit worked and they had some cleaning up to do before they earned their profit (e.g., they still owed Compound 112 wBTC). But of particular interest to us is how a call to mintWithEther was allowed to open such a bad position in the first place. As it turns out, the vulnerable code existed in the iTokens_loanOpeningFunctions.takeOrderFromiToken function, which was responsible for checking that the loan wouldn’t trigger liquidation (i.e., a position has fallen below margin) with a require statement:


require ((
        loanDataBytes.length == 0 && // Kyber only
        sentAmounts[6] == sentAmounts[1]) || // newLoanAmount
    !OracleInterface(oracle).shouldLiquidate(
        loanOrder,
        loanPosition
    ),
    "unhealthy position"
);

Note the use of a logical and followed by a logical or. Unfortunately, in the attacker’s transaction both sides of the logical and were true, which meant that shouldLiquidate never got called. You can trace through the transaction with a debugger and observe that:

  • iTokens_loanOpeningFunctions.takeOrderFromiToken gets called from LoanTokenLogicV4._borrowTokenAndUseFinal via a proxy (VZxProxy.0xb1eac3ad).
  • LoanTokenLogicV4._borrowTokenAndUseFinal gets called from LoanTokenLogicV4._borrowTokenAndUse.
  • LoanTokenLogicV4._borrowTokenAndUse gets called from LoanTokenLogicV4.marginTradeFromDeposit where the amountIsADeposit parameter is set to true and the loanDataBytes parameter is set to marginTradeFromDeposit’s own loanDataBytes parameter.
  • LoanTokenLogicV4.marginTradeFromDeposit ends up getting called when PositionTokenLogicV2.mintWithEther is called, which is the attacker’s main entry point into bZx.

In borrowTokenAndUse, if amountIsADeposit is set to true (as it is in this case), sentAmounts[6] gets set to sentAmounts[1]:


if (amountIsADeposit) { 
    (sentAmounts[1], sentAmounts[0]) = _getBorrowAmountAndRate( // borrowAmount, interestRate 
        loanOrderHash, 
        sentAmounts[1], // amount 
        useFixedInterestModel 
    ); 
    // update for borrowAmount 
    sentAmounts[6] = sentAmounts[1]; // borrowAmount

This meant that to skip the liquidation check, marginTradeFromDeposit simply needed to be called with an empty loanDataBytes parameter. And this is exactly what happened when mintWIthEther was called. A later fix removed the or operation so that !OracleInterface(oracle).shouldLiquidate is always called. This attack wasn’t especially sophisticated considering the simplicity involved in triggering the bug. However, the use of a flash loan amplified the impact and made exploitation far more lucrative.

The attack on bZx was a sign of things to come. Just days later a second exploit hit bZx, this time taking the form of an oracle attack, and dozens of attacks on other DeFi projects followed.

Today’s Flash Loan Attacks

Flash loan attacks continue to impact the DeFi ecosystem and there is no sign of them slowing down. Since 2020, flash loan attacks have cost companies hundreds of millions of dollars in losses. We continue to see creative exploit chains that leverage flash loans to increase payouts for threat actors. In 2023 alone there have been over $200,000,000 in losses directly attributable to them. One of the most impacted companies this year so far has been Euler, which suffered from a large flash loan attack that targeted missing business logic and validation when accounts performed donations using their protocol.

Today’s DeFi landscape continues to change as companies innovate by designing, developing and running new financial products. This innovation can often involve unforeseen risks though, especially as DeFi projects continue to grow in complexity and as part of an ever-increasing web of dependencies on libraries, protocols, and networks.

Preventing Future Flash Loan Attacks

Flash loans have augmented the potential impact of smart contract exploits and defenders must react accordingly. While writing code, it is important to follow secure coding best practices and implement smart contract patterns such as upgradability and versioning, circuit breakers, and rate limiting.

It is also crucial to rigorously test smart contracts and regularly perform vulnerability assessments. What this looks like in practice is keeping up to date with vulnerability trends, maintaining detailed documentation, writing tests, performing static analysis and fuzzing, and formally verifying critical functionality. This is why we were excited to see the development of Solidity’s built-in model checker – it provides developers with formal verification tooling out of the box.

Finally, having an outside team of security experts review your codebase can be an effective way to increase the likelihood of identifying critical vulnerabilities, such as the ones that we’ve seen exploited in flash loan attacks. Outside security experts can bring diverse auditing experience that may be difficult to foster internally within an organization. If an incident does happen, it is also important to have a team of highly skilled digital forensic professionals on call who can quickly and effectively help you respond.

Special thanks to https://www.palkeo.com/en/projets/ethereum/bzx.html and https://lev.liv.nev.org.uk/pub/bzx_debug.txt for some great initial analysis on the bZx attack.

Aon’s Thought Leader
  • Eric Rafaloff
    Technical Director, Security Testing, Cyber Solutions

About Cyber Solutions:

Aon’s Cyber Solutions offers holistic cyber risk management, unsurpassed investigative skills, and proprietary technologies to help clients uncover and quantify cyber risks, protect critical assets, and recover from cyber incidents.

General Disclaimer

This document is not intended to address any specific situation or to provide legal, regulatory, financial, or other advice. While care has been taken in the production of this document, Aon does not warrant, represent or guarantee the accuracy, adequacy, completeness or fitness for any purpose of the document or any part of it and can accept no liability for any loss incurred in any way by any person who may rely on it. Any recipient shall be responsible for the use to which it puts this document. This document has been compiled using information available to us up to its date of publication and is subject to any qualifications made in the document.

Terms of Use

The contents herein may not be reproduced, reused, reprinted or redistributed without the expressed written consent of Aon, unless otherwise authorized by Aon. To use information contained herein, please write to our team.