Cheese Bank Incident: Root Cause Analysis
In this blog, we analyze a Cheese Bank hack that occurred at 19:22:21 PM +UTC, Nov-6–2020. This hack was discovered while we review evil-will flashloans. This particular hack drains $3.3 million of USDC/USDT/DAI from Cheese Bank by exploiting a bug in its way to measure asset price from an AMM-based oracle. In the following, we elaborate the technical details.
Cheese Bank is a decentralized autonomous digital bank on Ethereum that allows investors to manage asset, including lending, fund management, insurance services etc. However, it has a flawed approach in the value measurement of collaterals based on the AMM-based oracle, i.e., Uniswap. As a result, with a flashloan-based manipulation of collateral price on Uniswap, the exploitation manages to make a series of malicious borrow operations, leading to $3.3 million of USDC/USDT/DAI loss (of Cheese Bank).
The decentralized finance (DeFi) ecosystem has rapidly grown to more than $16 billion in total value locked recently. With this exponential growth, incentives have increased for malicious actors to manipulate and attack vulnerable DeFi protocols. Among recent tools used within many DeFi attacks, flashloans allow users to open uncollateralized loans with the sole stipulation that the loan be paid back within the same transaction or it reverts. This is a significant departure from traditional DeFi lending, which often requires a user to over-collateralize a loan upfront.
In this month (of Nov. 2020), PeckShield researchers have reported two flashloan attacks, i.e., Akropolis and Value DeFi. In the string of attacks, we have seen malicious actors use flashloans to instantaneously borrow, swap, deposit and again borrow large numbers of tokens so they can artificially manipulate the price of a specific token on a single exchange (e.g., Uniswap, Curve). This sequence is essentially the foot in the door, allowing the attacker to then exploit that exchange’s anomalous pricing. In this blog post, we elaborate a recent incident happened to Cheese Bank — a decentralized autonomous digital bank based on a decentralized protocol on Ethereum.
The Hack Walk-through
We started the analysis from the transaction behind the hack: 0x600a…f1cc. This hack is initialized from a malicious contract (located at 0x9e02) and works as follows:
- Step 1: Take a flashloan of 21k ETH from dYdX
- Step 2: Swap 50 ETH to 107k CHEESE at UniswapV2
- Step 3: Add 107k CHEESE and corresponding 78 ETH into the liquidity pool at UniswapV2 and gets UNI_V2 LP tokens back.
- Step 4: Mint sUSD_V2 tokens with all LP tokens got from Step 3. This allows the exploit contract to use those LP tokens as collaterals for borrowing crypto assets from Cheese Bank.
- Step 5: Raise the CHEESE price at UniswapV2 by swapping 20k ETH to 288k CHEESE, which makes the UNI_V2 LP tokens more valuable as collaterals in Cheese Bank. This is the crucial step in this incident since the Cheese Bank uses the amount of WETH in a liquidity pool to estimate the price of the corresponding LP token. The manipulated UNI_V2-CHEESE-ETH pool (with 20k+ WETH) allows the bad actor to drain all the USDC, USDT, and DAI withheld by Cheese Bank by legit
- Step 6: Refresh the price feeds of Cheese Bank. The hacker intentionally invokes the
CheesePriceOracle::refresh()function to refresh the price of UNI_V2-CHEESE-ETH LP token which is derived from the amount of WETH in the liquidity pool and the ETH price derived from UNI_V2-USDT-ETH pool. Specifically, the
CheesePriceOracle::fetchLPAnhorPrice()function gets the
wEthBalanceof UNI_V2-CHEESE-ETH contract. With the passed in
totalValueis derived by
wEthBalance x 2 x ethPrice. Therefore, the unit price of UNI_V2-CHEESE-ETH LP token is computed by
totalValue / totalSupply of LP tokens. It means if a bad actor could somehow increase the amount of WETH in a pool (e.g.,
addLiquidity()with flashloan ether), the price of the LP token would be increased.
- Step 7: Drain the USDC, USDT, DAI withheld by Cheese Bank by
Besides the 2m USDC, 1.23m USDT and 87k DAI are
borrow()ed from Cheese Bank. Note that the hacker actually checks the balance before doing
borrow(). As shown in the following screenshot from https://oko.palkeo.com/, the exact balance of USDC/USDT/DAI are
borrow()ed by the exploit contract.
- Step 8: Swap 288k CHEESE back to 19.98k ETH at UniswapV2. Now, the bad actor harvests $3.3m equivalent USDC, USDT, and DAI. She needs to pay the 21k ETH flashloan back to dYdX with the assets in her pocket. The first choice is the really valuable 288k CHEESE which fills in \~20k ETH.
- Step 9: Swap 58k USDC to 132 ETH at UniswapV2. Since there’re some ether consumed in previous steps (e.g., fees paid to Uniswap), the bad actor needs to buy some more ether with USDC.
- Step 10: Collect the hacked assets into 0x02b7.
- Step 11: Return 21k ETH flashloan to dYdX
Once again, the AMM-based oracle allows the hacker to run away with
2m USDC + 1.23m USDT + 87k DAI + 2 ETH, which is around $3.3 millions loss. Since AMM-based oracles (e.g., Uniswap, Curve) are often the target behind many recent hacks, we suggest to exercise extra care when referencing them as oracle prices as they can be easily manipulated.
PeckShield Inc. is an industry leading blockchain security company with the goal of elevating the security, privacy, and usability of the current blockchain ecosystem. For any business or media inquiries (including the need for smart contract auditing), please contact us at telegram, twitter, or email.