bZx / Fulcrum (2020)

Two separate attacks within a week against the bZx lending protocol, totaling ~$1M. Modest by later standards, but historically significant: these were the first major flash-loan-funded exploits, and they established oracle manipulation as a primary DeFi attack pattern.

Timeline

  • February 15, 2020: First attack, ~$350K. Flash loan from dYdX → manipulated synthetic short on bZx → drained.
  • February 18, 2020: Second attack, ~$650K. Different vulnerability but same general pattern: flash loan → price manipulation → extract.

Root Cause

bZx's pricing for synthetic positions read directly from Uniswap V1 spot prices and Kyber Network rates. These spot prices were manipulable by any sufficiently-funded swap — and flash loans provided unlimited funding within a transaction.

Attack 1 (Feb 15)

  1. Flash-loan 10,000 ETH from dYdX.
  2. Open a 5x leveraged short on WBTC on bZx, funded with part of the flash loan.
  3. bZx, executing the short, swapped ETH→WBTC on Kyber, which routed through Uniswap V1.
  4. Because the swap was very large, the Uniswap price was driven significantly. bZx's own trade moved the market against bZx's own position, but bZx accounted for the WBTC value at the post-trade price, which was now much higher.
  5. Attacker, in the same transaction, swapped a smaller amount of WBTC→ETH at the inflated price (on Uniswap), profiting from the price gap.
  6. Repaid the flash loan.

Attack 2 (Feb 18)

A similar pattern but exploiting a different bZx integration. Flash-loaned funds, manipulated the Uniswap V1 ETH/sUSD pool, then borrowed against the manipulated price.

Exploit Path Detail (Attack 1)

The bZx code, at the time, called Kyber's KyberNetworkProxy for price quotes:

function getCurrentPrice(...) {
    (uint256 ethRate,) = kyber.getExpectedRate(...);
    // Used directly without sanity check
    return ethRate;
}

Kyber's quote at the moment of the call reflected the current Uniswap V1 price, which the attacker had just moved with their large swap. There was no comparison against a manipulation-resistant reference, no slippage cap on bZx's own swaps, no per-block trading limit.

What an Audit Should Have Caught

The auditor's question: where do prices come from, and can they be manipulated in a single transaction?

In 2020, "flash loans" were a known mechanism (Aave had launched them in January). The conceptual question "what happens if an attacker can pre-position a swap immediately before this call?" should have surfaced the bug. In retrospect, the bZx audit team and the protocol team did not connect the abstract "flash loan possibility" to the concrete "Uniswap V1 price can be manipulated by one swap."

Specific findings that should have appeared:

  1. Spot price used without sanity check. Any oracle reading from an AMM spot price is vulnerable to in-transaction manipulation.

  2. No slippage cap on internal swaps. bZx itself executed a very large swap as part of opening the leveraged position; the resulting price impact was not bounded.

  3. No same-block trading restrictions. A position could be opened and the protocol's spot price be used in the same transaction.

Lessons

  1. AMM spot prices are not oracles. A single transaction can move them arbitrarily. Use TWAPs, off-chain oracles (Chainlink, Pyth), or independent price sources for value-bearing decisions.

  2. Flash loans turn every economic vulnerability into a funded attack. A bug that requires "$10M of starting capital to exploit" effectively requires zero starting capital. Threat models that assumed expensive attackers are obsolete.

  3. Composition is the attack surface. bZx itself was correct in isolation; the bug emerged from how bZx composed with Kyber, which composed with Uniswap V1. Modern audits must consider composed behavior, not just per-contract behavior.

  4. Even modest exploits matter. $1M total across two incidents seems small, but the patterns established are exactly the patterns used in subsequent $100M+ exploits (Harvest Finance, PancakeBunny, Cream Finance, Beanstalk).

  5. Audit reports must enumerate trust assumptions on external dependencies. "We use Kyber for pricing" should immediately raise the follow-up: "what's Kyber's price source, and is it manipulation-resistant?"

The bZx attacks were the original sin of the flash-loan exploit era. Every flash-loan attack from 2020-2026 traces lineage to this pattern. The defense — manipulation-resistant pricing — is well-known. The pattern keeps appearing because new protocols keep using spot AMM prices anyway.