Reth.deposit() uses Uniswap V3 to trade WETH to rETH, when it's impossible to deposit into RocketPool and that's the case at the time of writing this report. The problem is that it uses spot price from IUniswapV3Pool.slot0, which is susceptible to manipulation. The protocol protects against that by adding maximum slippage, but this won't work if a MEV bot sandwitches the transaction, lowering the price a lot. Then the victim's transaction slippage will be calculated using manipulated price, allowing for higher price slippage than expected.
Impact
Users loose significant amounts of rETH on slippage.
Proof of Concept
MEV bot sees large deposit and sandwiches it, first driving the price down by 10%. This is relatively easy, because rETH / ETH Uni V3 pool is highly concentrated. The price manipulation won't revert the victim's deposit, because the minPrice is taken from Reth.poolPrice() , which returns spot price (already manipulated) from Uniswap:
Lines of code
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L156 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L176-L183 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L236-L241
Vulnerability details
Reth.deposit()
uses Uniswap V3 to trade WETH to rETH, when it's impossible to deposit into RocketPool and that's the case at the time of writing this report. The problem is that it uses spot price fromIUniswapV3Pool.slot0
, which is susceptible to manipulation. The protocol protects against that by adding maximum slippage, but this won't work if a MEV bot sandwitches the transaction, lowering the price a lot. Then the victim's transaction slippage will be calculated using manipulated price, allowing for higher price slippage than expected.Impact
Users loose significant amounts of rETH on slippage.
Proof of Concept
MEV bot sees large deposit and sandwiches it, first driving the price down by 10%. This is relatively easy, because rETH / ETH Uni V3 pool is highly concentrated. The price manipulation won't revert the victim's deposit, because the minPrice is taken from
Reth.poolPrice()
, which returns spot price (already manipulated) from Uniswap:It's then used to calculate
minOut
:This way a user is stolen from rETH part of the deposit.
While testing this, I was even able to DoS the transfer in
Reth.poolPrice()
function in lines:with 2000 ETH. Then, sqrtPriceX96 becomes circa
1461446703485210103287273052203988822378723970341
, which overflows and DoSes the transfer.Running tests
In order to run the test, get foundry files from https://gist.github.com/deliriusz/5eb92d3138db9943cf6da06caa6c0aa0 .
then run the test using
forge test -vvv -m "sand" --fork-block-number 16933064 --fork-url $MAINNET_URL
the test case:
Tools Used
Manual analysis
Recommended Mitigation Steps
Use Uniswap TWAP to get current price of rETH