code-423n4 / 2023-03-asymmetry-findings

14 stars 12 forks source link

If rETH deposit is closed, SafETH can be exploited #370

Closed code423n4 closed 1 year ago

code423n4 commented 1 year ago

Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L170-L183 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L63-L129

Vulnerability details

Impact

When staking ETH for SafETH, rETH deposit can get routed through uniswap v3 pool if the rETH contract doesn't have enough space for the staked ETH.

When routing through uniswap, the minOut is only determined by the spot price that can be changed prior to calling staking.

On top of that, the stake function mint SafETH by calculating the backing of the protocol. rETH backing being determined by uniswap spot price, an attacker can increase or decrease the assumed backing per rETH on staking and get more safETH than deserved and unstake more ETH than staked.

Proof of Concept

The current uniswap pool used by the code is this pool (0.05% fee): https://info.uniswap.org/#/pools/0xa4e0faa58465a2d369aa21b3e42d43374c6f9613

At the date of this report it has ~5m$ liquidity in ETH/rETH but most of it is provided in a very tiny range between 1.0833 ETH per rETH and 1.0408 ETH per rETH.

The deposit function of rETH is initiallized at a 1% maxSlippage so if attacker went outside the range given above, the maxSlippage of the trade would probably be reached BUT attacker could on top of changing the ratio of the pool, add liquidity in a very tiny range where the deposit swap is supposed to happen to make maxSlippage useless.

The given example is approximative and values are not always accurate, more calculation would be needed to execute it correctly.

Here is the scenario of the attack:

Current state of SafETH would be 500 SafETH for a backing of 550 ETH with a 1/3 of it in each derivative: rETH, SfrxETH, WstETH. All with 1 - 1 ratio.

Pool is now at a price of 0.0001 ETH per rETH on the position we added liquidity at.

The estimated backing per SafETH is now estimated by the protocol to be : 183.33 ETH for WstETH + 183.33 ETH for SfrxETH + 183.33 * 0.0001 = 0.01833 ETH for rETH So total backing is 366.6783 ETH for 500 SafETH, result in 0.7333 ETH per SafETH.

66.6666 (200 / 3 derivative) ETH are routed to each derivative

rETH is routed on uniswap in the range we added liquidity at so protocol get 0.006666~ rETH back.

Attacker receives 200 / 0.7333 = 272.7 SafETH.

Protocol now has 250 WstETH, 250 SfrxETH and 183.33 + 0,006666 rETH.

250 0.3529 = 88.225 WstETH 250 0.3529 = 88.225 SfrxETH 183.3 * 0.3529 = 64.68657 rETH

Given backing at the beggining of 1 - 1 this results in 241.13 ETH for the Attacker.

Attacker paid 0.05 / 100 * 1200 = 0.6 ETH in swap fees and 0.6 rETH (because swapped both ways.

Attacker reimburse 1200 rETH 0,09 / 100 = 1.08 rETH extra Attacker reimburse 200 ETH 0.09 / 100 = 0.18 ETH extra

Attacker is left with 38.67 ETH.

Tools Used

Manual review

Recommended Mitigation Steps

rETH deposit should be using a twap price or an oracle to make this attack more difficult.

code423n4 commented 1 year ago

Withdrawn by HHK