The Cream Hacks

Composability within DeFi Lending (part II)

DefiDuck
4 min readNov 2, 2021

Introduction

Ola.finance is a platform for creating and managing isolated lending networks. Compound and Cream are examples of isolated lending networks, each one with its own configuration and composition of tokens. Isolation means that when Cream gets hacked Compound is not affected and vice versa. However, in a given lending network, it is enough to have a single problematic token listed (eg. yUSD or AMP) to cause all users, even those who had no direct interaction with the problematic token (ie. users who did not deposit or borrow it) to lose funds. Thus we say that a lending network is as strong as its weakest link.

While lending network creators under the Ola platform are free to list whichever tokens they’d like, Ola is responsible for implementing price oracles for these tokens. In order to make sure that we provide reliable price feeds, and list only tokens that are compatible with the design of our lending networks, we study the last two hacks in Cream and make our best effort to draw the necessary conclusions.

In these hacks, the attackers exploited specific mechanisms in AMP and yUSD in order to trick the system into letting them borrow higher value relative to what they had deposited. These hacks were a result of composability issues — these tokens shouldn’t have been vanilla-listed in Cream (or in any Compound-like lending network).

In the previous post we analyzed the yUSD hack and in this post we’ll discuss the AMP hack.

AMP hack

Main heist transactions

The attacker made 10 transactions that can all be found on this Etherscan page (look for the method start()).

The two most profitable transactions were: https://etherscan.io/tx/0x6f3bc128724bdae00f328e300d74103d0adcf988720b0b02f65b6f258af276fa https://etherscan.io/tx/0xf0f6a07e0d27b00b972ccdfa500cb53f87ff3e079a3746afb3af6ca84a186043

ERC-777

This attack is sometimes referred to as a reentrancy attack, but it really isn’t classic reentrancy, where the attacker is able to make a recursive call back to the original function they had triggered beforehand. Cream’s cTokens have a reentrancy guard that prevents this. However, due to the way that the AMP token implements its transfer() function the attacker was able to borrow from Cream’s ETH market while a borrow function was in the midst of being executed in the AMP market.

To understand this attack, we need to understand ERC-777 hooks (AMP is actually not an ERC-777, but it implements the same token transfer hooks on send and receive). The idea is very simple — when an ERC-777 token transfers tokens from one address to another, it triggers a callback function within the receiver’s address. This function can now run arbitrary logic. This mechanism is useful for many things, for example to allow the receiver to reject the transfer.

Attack steps (following the first transaction we linked above)

Get crETH. The attacker flash-loans 4,000 WETH (worth around $12.8M) from a UniV2 pair, withdraws the ETH and deposits it in Cream’s ETH market. He now holds 4,000 ETH worth of crETH.

Borrow AMP. The attacker now borrows 155,840,000 AMP (just over $8.5M at the time of the attack) tokens from Cream’s AMP market.

Borrow ETH while the borrow AMP function executes. After Cream’s Comptroller approved the borrow request, but before the attacker’s borrow balance was updated, the crAMP contract sends the attacker its borrowed AMP tokens, and within this transfer, the attacker’s tokensReceived() callback function is called (as explained above). The attacker borrows 2,840 ETH (~$9M) from Cream within the callback function and since the previous borrow wasn’t registered, the Comptroller approves this borrow request. After this borrow is complete, the execution engine goes on to register the first borrow. The attacker was able to borrow 155,840,000 AMP and 2,840 ETH against its 4,000 ETH deposit. This is about twice the value he should have been able to borrow.

Liquidate own underwater position. The attacker now uses 77,920,000 of the AMP tokens he borrowed to liquidate his own position and get 1,469.846987793098019422 ETH for them.

Repay flash-loan. The attacker transfers 4,012.4 WETH back to the pair he took the flash-loan from and completes the heist.

Bottom line

The attacker ends up with 1,469.8469+2,840–4,012.4=297.4469 WETH and 155,840,000–77,920,000=77,920,000 AMP, worth around $5.25M (from this transaction alone).

All in all, the attacker ends up with:

WETH: 69.5827+148.7232+297.4469+297.4471+148.72393+148.7243+ 78.2063+78.2064+41.0334 = 1,308.0946

AMP: 19,480,000+38,960,000+77,920,000+77,920,000+38,960,000+ 38,960,000+19,480,000+19,480,000+9,740,000 = 340,900,000

And in $ terms this sums up to ~$23M at the time of the attack.

Conclusion

This attack was a result of reckless integration of AMP into Cream. As it stands AMP or any other ERC-777 tokens are not compatible for listing in Compound-like lending networks. One small change that can be made is to register loans in the cToken’s state before it sends out the funds. This would have prevented this attack from being possible. However, a better design altogether might be to let users do whatever they want in an optimistic fashion, and only at the very end of the execution, to check that their final position is valid.

--

--