Mai Finance’s Oracle Manipulation Vulnerability Explained
On Oct 18th 2022, the blockchain security team at Amber Group discovered a critical vulnerability in two Mai Finance’s vaults (SCSEMVT and YCSEMVT). This vulnerability would allow an attacker to manipulate the collateral price, borrow all the funds from the pool, and walk away with bad debts. We reported the vulnerability to Mai Finance and they fixed it in time. In this blog post, we elaborate on the details of our findings.
Mai Finance / QiDao
Mai Finance is a zero-interest lending protocol which allows users to borrow stablecoin ($MAI) at 0% interest with collateral. Anyone can create QiDao vaults and deposit collateral to borrow against the collateral’s value. Note that Mai Finance is an overcollateralized stablecoin protocol; loans are secured by always having more value locked than the amount of debt. For example, the Yearn Curve stETH Ethereum MAI Vault (YCSEMVT) accepts the $steCRV as collateral and lends $MAI.
In mid-October 2022, ChainSecurity disclosed a new form of reentrancy bugs in Curve. The so-called read-only reentrancy vulnerability allows an attacker to reenter the oracle function, get_virtual_price(), through a crafted smart contract and get a pumped LP token price back, which creates room for bad actors to make profits. Specifically, a lending protocol integrated with Curve LPs usually uses the oracle function provided by Curve and called get_virtual_price() to retrieve the price of an LP token derived from the pegged assets of a stable swap pool.
As shown in the code snippet above, we have the D value to represent the balances of all underlying assets. And, the virtual price is computed by dividing D with the total supply of the LP token. So, if it happens that the D value is not synchronized with the total supply, the reported price would be distorted. There’s one case in which the price distortion occurs — remove_liquidity().
Below is a code snippet from the remove_liquidity() function of the ETH/stETH pool. In line 8, it burns the tokens and leads to the decrease in the total supply. In line 16, the underlying asset, ETH, is transferred to the user. Due to the properties of ETH transferring, the control flow gets into the recipient’s fallback function if the recipient is a smart contract.
For now, the attacker can hijack the execution flow and do anything he/she wants. What if the attacker issues another get_virtual_price() call in the first line of his/her fallback function? With the total supply of Curve LP token decreased and the balances not updated, the virtual price rises. Then, the manipulated price could be used to withdraw another batch of underlying assets or even drain some pools.
As one of the victims of the new read-only reentrancy attack, QiDao’s YCSEMVT vault adapted get_virtual_price() as one of the sources to evaluate the collateral price (line 17 below).
As explained earlier, a bad actor could amplify the collateral price and borrow $MAI tokens in an undercollateralized way. We will show you the details in the following sections.
Based on the price manipulation concepts exemplified above, we can construct an exploit contract as follows:
- Add large amounts of liquidity to the Curve ETH/stETH pool.
- Deposit collaterals to the vault.
- Remove the liquidity from the Curve ETH/stETH pool. The ETH transfer triggers the fallback function.
- Hijack the remove_liquidity call and borrow the tokens.
The code snippet of the exploit contract below shows how we simulate the attack.
Note that we remove the liquidity with the remove_liquidity_imbalance() function which is more powerful than remove_liquidity(). In particular, the remove_liquidity_imbalance() function allows users to withdraw LP tokens from the pool in an imbalanced amount. For example, we can simply trigger the fallback function by removing only 1wei ETH from the pool.
The following screenshot proves our theory of the attack. An attacker could walk away with a profit of $15k worth of $MAI in our experiment with the pumped virtual price and the collateral price by exploiting the vulnerability we identified.
Timeline & Mitigation
Oct 18th — Amber Group found the vulnerability and finished the Proof of Concept (PoC) exploit internally.
Oct 19th — Amber Group contacted the QiDao team.
Oct 20th — The QiDao team confirmed the issue and came up with a fix.
Oct 20th — The QiDao team deployed a new oracle contract.
Oct 24th — The same vulnerability was exploited on polygon, which was not related to QiDao as we confirmed with the QiDao team.
Dec 20th — The QiDao team announced the price feed upgrade on the stETH-ETH vault with public acknowledgements to ChainSecurity and Amber Group.
The information contained in this post (the “Information”) has been prepared solely for informational purposes, is in summary form, and does not purport to be complete. The Information is not, and is not intended to be, an offer to sell, or a solicitation of an offer to purchase, any securities. The Information does not provide and should not be treated as giving investment advice. The Information does not take into account specific investment objectives, financial situation or the particular needs of any prospective investor. No representation or warranty is made, expressed or implied, with respect to the fairness, correctness, accuracy, reasonableness or completeness of the Information. We do not undertake to update the Information. It should not be regarded by prospective investors as a substitute for the exercise of their own judgment or research. Prospective investors should consult with their own legal, regulatory, tax, business, investment, financial and accounting advisers to the extent that they deem it necessary, and make any investment decisions based upon their own judgment and advice from such advisers as they deem necessary and not upon any view expressed herein.