Platform: Code4rena
Start Date: 30/04/2024
Pot Size: $112,500 USDC
Total HM: 22
Participants: 122
Period: 8 days
Judge: alcueca
Total Solo HM: 1
Id: 372
League: ETH
Rank: 23/122
Findings: 3
Award: $599.00
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: guhu95
Also found by: 0rpse, 0x007, 0x73696d616f, 0xCiphky, 0xabhay, Audinarey, Bauchibred, Fassi_Security, GalloDaSballo, GoatedAudits, KupiaSec, LessDupes, MSaptarshi, OMEN, Ocean_Sky, RamenPeople, SBSecurity, Tendency, WildSniper, aslanbek, bill, blutorque, crypticdefense, cu5t0mpeo, d3e4, gjaldon, grearlake, gumgumzum, honey-k12, ilchovski, jokr, josephdara, kennedy1030, p0wd3r, peanuts, stonejiajia, t0x1c, tapir, underdog, zzykxx
0.4071 USDC - $0.41
Holders of wBETH and other tokens that will be accepted by Renzo can compensate for their losses resulting from price drop (e.g. slashing) using the funds of Renzo users. Very quickly after this system weakness will be discovered by the first attacker, it can be automated and exploited by bots as an arbitrage opportunity creating a growing loss for Renzo depositors.
Renzo creates a system that allows for restaking various tokens and receive their Liquid Restaking Token (LRT) EzETH in return. Example ERC20 used by the protocol ezETH, stETH, wBETH.
Users depositing their funds into Renzo are encouraged by the rewards of staking and re-staking through EigenLayer, but they also bear the risk of penalties and slashing of their deposited funds.
However, in case of wBETH, the 3rd party users who are not associated in any way with Renzo ecosystem can take advantage of such LRT and make Renzo users bear their losses.
Keeping in mind these things:
Consider the following scenario (values used for ease of calculation and to illustrate the attack, real values will be presented later in this description):
200 ETH is deposited inside Renzo by users and 200 ezETH were minted.
The attacker (wBETH staker) has 100 wBETH (price is e.g. 1 wBETH = 2 ETH, their wBETH is worth 200 ETH)
The attacker knows through monitoring slashing events and big withdrawalas that price will drop soon.
The attacker deposit their 100 wBETH to Renzo to get 200 ezETH (as current price is still 1 wBETH = 2 ETH)
Total value locked on Renzo will increase from 200 ETH to 400 ETH (200 eth and 100 wBETH). Users posses 200 ezETH, Attacker posses 200 ezETH.
Total value locked on Renzo will decrease from 400 ETH to 300 ETH (as 100 wBETH is now worth only 100 ETH). That causes value drop for ezETH value (1 ezETH = 0,75 ETH).
Attacker gets 200 wBETH back (current price is 100 ETH) and additional 50 ETH.
Attacker buys additional wBETH for their additional 50 ETH, so know they have 250 wBETH (from another source, outside protocol)
Now price recover, so its again 1 wBETH = 2 ETH.
Attacker now have 250 wBETH worth 500 ETH, and Renzo users have 150 ETH (lost 50 ETH, as attacker delegeted their risk to Renzo users).
However, the price will not drop by 50%. The real numbers could be up to 10%.
wBETH (https://coinmarketcap.com/currencies/wrapped-beacon-eth/) there are price drops (https://coinmarketcap.com/currencies/wrapped-beacon-eth/historical-data/)
Looking also at 2 examples of similar assets to those that are considered (cbETH and wstETH) we can observe the following:
cbETH (https://coinmarketcap.com/currencies/coinbase-wrapped-staked-eth/) there are price drops, based on data from last 365 days the biggest percentage drop in price occurred on March 11, 2023, with a drop of approximately 8.25% (https://coinmarketcap.com/currencies/coinbase-wrapped-staked-eth/historical-data/)
wstETH (https://coinmarketcap.com/currencies/lido-finance-wsteth/) there are price drops, based on data from last 365 days the biggest percentage drop in price occurred also on March 11, 2023, with a drop of approximately 9.28% (https://coinmarketcap.com/currencies/lido-finance-wsteth/historical-data/)
Manual Review
Set a minimum period for the user between their deposit and withdrawal so that they cannot take advantage of price fluctuations. Right know, their redeem amount is calculted as soon as withdraw is called.
Other
#0 - c4-judge
2024-05-16T13:59:56Z
alcueca marked the issue as not a duplicate
#1 - c4-judge
2024-05-16T14:00:06Z
alcueca marked the issue as duplicate of #326
#2 - c4-judge
2024-05-17T12:48:32Z
alcueca marked the issue as satisfactory
🌟 Selected for report: LessDupes
Also found by: RamenPeople, SBSecurity, bill, guhu95, ilchovski, peanuts
598.5522 USDC - $598.55
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L491 https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/Withdraw/WithdrawQueue.sol#L206
Loss of capital of users who withdraw stETH. Their LST rewards will be miscalculated. Loss will constantly grow over time.
The general questions section in the readme.md states that the ERC20s used by the protocol are ezETH, stETH, and wBETH. https://github.com/code-423n4/2024-04-renzo/blob/main/README.md#scoping-q--a
However, stETH is a rebase token and the amount of stETH in a user/platform balance will not be constant - it changes daily as staking rewards arrive.
The current deposit/withdraw flow does not handle rebasing tokens at all.
In the case of wrapped tokens such as used here wBETH, although the exact amountToRedeem
amount is calculated during the withdrawal request WithdrawQueue#L229 user will continue to earn part of rewards as their value is determined by price that increases over time.
Even though the user has to wait 7 days (according to the readme) for a claim, the same amountToRedeem
of wBETH will be worth more after this 7 days.
In the case of stETH, rebase token the exact amountToRedeem
will be calculated in the same way. However, rebase token value remains 1:1 and its value changes through balance updates, not increasing the price.
Here, after 7 days, this amountToRedeem
will not be updated. Users who decide to withdraw stETH will always lose their accumulating rewards before they will be able to claim.
Manual Review
Consider the non-rebasing wrapped version of stETH instead (wstETH) or re-calculate amountToRedeem to adjust it's balance.
https://help.lido.fi/en/articles/5231836-what-is-lido-s-wsteth
Other
#0 - c4-judge
2024-05-17T12:46:27Z
alcueca marked the issue as duplicate of #326
#1 - c4-judge
2024-05-17T12:48:14Z
alcueca marked the issue as satisfactory
#2 - c4-judge
2024-05-27T08:46:52Z
alcueca marked the issue as not a duplicate
#3 - c4-judge
2024-05-27T08:46:57Z
alcueca changed the severity to 2 (Med Risk)
#4 - c4-judge
2024-05-27T08:47:08Z
alcueca marked the issue as duplicate of #282
#5 - c4-judge
2024-05-27T08:51:57Z
alcueca changed the severity to 3 (High Risk)
#6 - alcueca
2024-05-27T08:55:58Z
While this issue doesn't point out that deposits and withdrawals will revert on balance decreases of stEth due to slashing, it points out that it shouldn't be treated as non-rebasing LSTs in the withdrawal queue.
🌟 Selected for report: 0xCiphky
Also found by: 0rpse, 0x007, 0xAadi, 14si2o_Flint, ADM, Aamir, Aymen0909, BiasedMerc, DanielArmstrong, Fassi_Security, FastChecker, KupiaSec, LessDupes, MaslarovK, Neon2835, RamenPeople, SBSecurity, Shaheen, Tendency, ZanyBonzy, adam-idarrha, araj, b0g0, baz1ka, bigtone, bill, blutorque, carrotsmuggler, cu5t0mpeo, fyamf, gesha17, gumgumzum, hunter_w3b, inzinko, jokr, josephdara, kennedy1030, kinda_very_good, lanrebayode77, m_Rassska, mt030d, mussucal, tapir, underdog, xg, zzykxx
0.0402 USDC - $0.04
https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L562 https://github.com/code-423n4/2024-04-renzo/blob/519e518f2d8dec9acf6482b84a181e403070d22d/contracts/RestakeManager.sol#L549
Under specific conditions, user deposits will always revert.
When a user deposits funds through RestakeManager
, the function checks the withdraw buffer and fill it if it is below the buffer target determined by bufferToFill
.
If it needs to be filled, line RestakeManager#L547 checks whether the transferred _amount
is less than or equal to bufferToFill
.
bufferToFill = (_amount <= bufferToFill) ? _amount : bufferToFill;
The case where _amount
is equal to bufferToFill
is incorrectly handled as after subtraction the _amount
value will be 0 RestakeManager#L549.
_amount -= bufferToFill;
That leads to a revert during operatorDelegator.deposit
line RestakeManager#L562, as it will not be able to pass the requirement for tokenAmount
in OperatorDelegator#L147
if (address(tokenStrategyMapping[token]) == address(0x0) || tokenAmount == 0)
Manual Review
Handle separately the case in which the entire _amount
goes towards filling the bufferToFill
(_amount == bufferToFill
). If the amount equals 0 after paying bufferToFill
, do not try to deposit it to operatorDelegator
, allocate the appropriate amount of tokens to the user.
Token-Transfer
#0 - c4-judge
2024-05-20T05:02:09Z
alcueca marked the issue as satisfactory