Cream Finance was hacked again on 27th October 2021. Likely for the final time. The attacker stole $130m+ worth of assets from Cream’s lending protocol. The attack was executed over multiple transactions due to block gas limits, but the bulk of it happened in this transaction.
This was one of the most sophisticated and cleanly executed DeFi attacks. The summary of the attack is that the attacker borrowed $1.5b of Yearn’s yUSD vault shares against $2b worth of collateral. They then doubled the value of the shares atomically by donating yUSD to the yearn vault. This meant that their debt on Cream became $3b against a $2b collateral. They can now default and take home a sweet $1b profit. Cream only had $130m assets available for lending, so the attacker was limited to $130m profits.
- Using account A, Flash Borrow $500m DAI from Maker.
- Deposit $500m dai into yDAI Yearn Vault.
- Deposit $500m yDAI into yUSD Curve pool.
- Deposit $500m yUSD into yUSD Yearn Vault (Will call it yUSDVault from now on). The total supply of yUSDVault was $11m before this and became $511m after the mint.
- Mint $500m cryUSD by depositing $500m yUSDVault into Cream. This cryUSD is really going to make Cream cry later. Account A now has $500m cryUSD.
- Using account B, flash borrow $2b ETH.
- Mint cETHER by depositing $2b ETH in Cream.
- Borrow $500m yUSDVault by using the $2b ETH as collateral.
- Mint $500m cryUSD by depositing the $500m yUSDVault back in Cream.
- Transfer $500m cryUSD to account A. Account A now has $1b cryUSD.
- Again, Borrow $500m yUSDVault by using the $2b ETH collateral. Debt becomes $1b against a $2b collateral so – all good.
- Mint $500m cryUSD by depositing the $500m yUSDVault back in Cream.
- Transfer $500m cryUSD to account A. Account A now has $1.5b cryUSD.
- Again, Borrow $500m yUSDVault. Debt becomes $1.5b against a $2b collateral – all good.
- Transfer $500m yUSDVault to account A. Account A now has $1.5b cryUSD and $500m yUSDVault.
- Everything worked as expected till now. The fun part begins now.
- To make the next steps more efficient, get as much of yUSDVault total supply under your control as you can. This step, specifically, indicates that the attacker was someone with deep DeFi knowledge. They not only pulled off a large heist, but they did it efficiently using their deep knowledge of the space.
- DefiDollar was holding $3m of yUSDVault as collateral against DUSD.
- Using account A, Buy $3m DUSD from Curve.
- Redeem/burn $3m DUSD for the underlying yUSDVault collateral. Account A now has $1.5b cryUSD and $503m yUSDVault.
- Redeem/burn $503m yUSDVault shares for the underlying yUSD tokens. This reduced the total supply of yUSDVault to $8m.
- The value of yUSDVault shares is calculated as (contract’s yUSD balance) / (total supply of yUSDVault shares). At this moment, the yUSDVault had a total supply of $8m and yUSD balance of $8m. This meant that the price of yUSDVault share was $1.
- Based on $1 price, account A has $1.5b cryUSD. Account B has a debt of $1.5b cryUSD against $2b of ETH collateral.
- The magic happens here – Transfer 8m yUSD to yUSDVault. This basically donates $8m to the yUSDVault. The yUSD balance becomes $16m while total supply remains $8m. Therefore, the price of every yUSDVault share becomes $2 for real. All holders of yUSDVault just doubled their investments.
- Since yUSDVault is now worth double, Cream now thinks that the account A cryUSD is now worth $3b instead of the original $1.5b. Technically, this is true. The vault of yUSDVault shares really did double. There’s no price or oracle manipulation here and don’t let chainlink god tell you otherwise :).
- The problem is that account B’s debt suddenly increased to $3b against collateral of just $2b. Account B can now default on the loan and “pocket the $1b profit”. All is not good anymore. In normal circumstances where price of assets changes slowly, the system would’ve liquidated the account B before its debt became more than the collateral. Liquidation isn’t possible here because the price jump happened atomically. Using a TWAP or other time delayed oracle wouldn’t have helped either because you still wouldn’t have been able to liquidate the user. To liquidate a user, you need to buy out their debt position using their collateral. Nobody would have sold $3b worth of yUSDVault to Cream for $2b of ETH. Delaying your oracle input doesn’t mean someone will magically accept your trade at delayed prices.
- Using the $3b of cryUSD collateral in account A, borrow $2b ETH.
- Account A now has $2b ETH, ~$500m yUSD (from redeeming yUSDVault shares), and $1b in Cream collateral ($3b cryUSD collateral minus $2b ETH debt).
- Use the ETH and yUSD to pay back the flash loans and utilize the remaining $1b collateral to borrow/steal whatever is left in Cream ($130m, in this case).
Observations and Theories
Put on your tinfoil hats because I’m gonna become Sherlock for a bit. Take anything posted after this with a bucket of salt.
- The attacker deposited the USDT and USDC into Curve pool to avoid getting blacklisted by these centralized stablecoins. This further proves that they are an experienced DeFi dev rather than a script kiddie.
- The attacker deposited some renBTC into the ren bridge to bridge them to Bitcoin, diversifying their laundry.
- The attacker used Paraswap rather than 1inch for trades. 1inch is known to collect information about users and has helped doxx attackers in the past. Perhaps the attackers didn’t want to risk it. However, I think they are just farming potential airdrop from Paraswap. Again, showing their deep DeFi knowledge.
- The attacker did some transactions using EIP1559 and others without. It’s possible that they used scripts for some, and those scripts didn’t support EIP1559, but it’s also possible that two different people shared the key.
- The attacker later split their loot equally into two accounts, reinforcing the theory that two people are involved.
- Cream forked Compound’s code and gave them 25% of Cream tokens to onboard them as security advisors, but they couldn’t fork/force them to actually work for Cream. This was not a smart contract hack but a governance hack. The governance listed a token that should not have been listed. Cream/Compound (and Aave, Iron bank etc) cannot support tokens with such volatile price (yUSDVault price can atomically increase to infinity).
- Cream got exploited in August because of a similar reason – they listed AMP token which allowed reentrancy. Another governance failure. You can fork code but not talent – especially not talent’s intent.
- When you have shared borrowing/lending markets, even a single bad asset can lead to a total collapse. Isolated markets like the one provided by Kashi protocol from Sushi limit the risk of a bad asset to just that asset’s borrowers and lenders. Hence, you can list more exotic assets on Kashi with lower risk.
- This attack was cleanly executed, and the contracts used for the attack seem well built. I lost a small amount of xSUSHI in this attack, but I’m impressed by the attacker’s skills. I wish they used these skills for good rather than evil though. The attacker reminds me of Kevin Mitnick’s early days.
ps This post is condensed with information that might not be very well explained. I have to get back to my day job now but other writers are welcome to use this information to create easier-to-understand articles.