code-423n4 / 2024-04-renzo-validation

2 stars 2 forks source link

Claiming withdrawals may change the ezETH price #131

Closed c4-bot-8 closed 6 months ago

c4-bot-8 commented 6 months ago

Lines of code

https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Withdraw/WithdrawQueue.sol#L246 https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Withdraw/WithdrawQueue.sol#L299-L309

Vulnerability details

Impact

Claiming withdrawals may change the ezETH price. This can be used extract profit from the protocol, at the cost of other holders.

Proof of Concept

The price of ezETH is essentially TVL / totalSupply, i.e. depositing d worth of collateral will mint d * totalSupply / TVL ezETH (RenzoOracle.calculateMintAmount()). When requesting to withdraw w ezETH the reverse calculation is made, i.e. w * TVL / totalSupply (RenzoOracle.calculateRedeemAmount()). These values are set at the time of the withdrawal request, such that the amount of collateral token and amount of ezETH to burn are fixed. Only later upon claim() are the tokens transferred and the ezETH burned.

The values used in the deposit calculation are also not affected by any pending withdrawals, but the current TVL and the current ezETH supply are used.

Suppose the value of the collateral token to be withdrawn has deviated from the average collateral value, i.e. the price of ezETH, since the withdrawal request was made. If it is greater than the average then upon claim() correspondingly more value will be removed from the TVL than ezETH burned from the total supply, and the price of ezETH will decrease. Conversely, if the value of the collateral token has become less than the average of the collateral values then the price of ezETH will increase.

This can be abused in all kinds of ways. It gives an attacker some measure of control over the price of ezETH. He can make withdrawal requests and then choose to claim them such that the price is manipulated in his favour, depending on the integrations with Renzo. The attacker can frontrun another claim (not necessarily his own) with a deposit such that he experiences an immediate profit from the price movement.

For example, let's say 2 ezETH has been minted against 1 tokA and 1 tokB, and that all current prices are 1. Suppose 0.5 ezETH is requested to be withdrawn against tokA. This will schedule the burning of 0.5 ezETH and the transfer of 0.5 tokA. Suppose then the price of tokA increases to 1.1 and the price of tokB increases to 1.25. Before claim() the price of ezETH is (1.1 + 1.25) / 2 = 1.175, but after claim() the price would be (0.55 + 1.25) / 1.5 = 1.2. Now, if an attacker deposits 1 ETH's worth (of any token) just before claim() is called, he will receive 1 / 1.175 ≈ 0.8512 ezETH. After claim() the price will then be (0.55 + 1.25 + 1) / 2.3512 ≈ 1.1910. Immediately withdrawing this again would give him 0.8512 * 1.1910 ≈ 1.014 ETH worth of collateral. Note that by sandwiching the claim() with a deposit and withdrawal the attacker was able to reduce the price of ezETH (up to future additional price changes) and extract a profit. The more volatile the collateral tokens, and especially if they move in opposite directions, the more profit can be extracted.

Recommended Mitigation Steps

Consider calculating the amount of collateral token to be transferred at the time of claim() instead. It could then be possible to also choose which token to withdraw from in claim(). That is, a withdraw request would simply be a request to withdraw an amount of ezETH, with the rest to be decided upon claim().

Assessed type

MEV

raymondfam commented 6 months ago

@howlbot accept

raymondfam commented 6 months ago

Price advertently/inadvertently manipulated as described indeed.