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: 100/122
Findings: 1
Award: $0.04
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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/Delegation/OperatorDelegator.sol#L147-L148
When a user deposits ERC20 using the deposit
function of the RestakeManager contract
, the system checks whether the WithdrawQueue
contract's withdrawal buffer needs to be filled:
uint256 bufferToFill = depositQueue.withdrawQueue().getBufferDeficit( address(_collateralToken) );
When the value of bufferToFill
is greater than or equal to the user's deposit amount, the user's ERC20 deposit will be transferred to the WithdrawQueue
contract, resulting in the deposit
function of the OperatorDelegator
contract being called with an input parameter _amount
equal to 0. The deposit
function of the OperatorDelegator
contract is as follows:
function deposit( IERC20 token, uint256 tokenAmount ) external nonReentrant onlyRestakeManager returns (uint256 shares) { if (address(tokenStrategyMapping[token]) == address(0x0) || tokenAmount == 0) revert InvalidZeroInput(); // Move the tokens into this contract token.safeTransferFrom(msg.sender, address(this), tokenAmount); return _deposit(token, tokenAmount); }
If the tokenAmount
is equal to 0, the deposit
function of the OperatorDelegator
contract will revert, resulting in the rejection of the entire ERC20 deposit transaction.
Suppose the withdrawalBufferTarget
value of stETH
in the WithdrawQueue contract
is equal to 100.
A large investors user or institution withdraws 32 stETH
normally, then the WithdrawQueue contract has a 32 stETH withdrawal buffer that needs to be filled, which means the getBufferDeficit(stETH)
return value is equal to 32.
At this time, any other user's deposit amount less than or equal to 32 stETH
will be rejected.
I found that most retail investors participating in Renzo protocol deposits have relatively small amounts(such as 0.1 stETH, 3 stETH, and other small amounts), so most retail investors may not be able to deposit. Only when new large investors or institutions participate in stETH deposits (or rewards from EigenLayer) and supplement the stETH in the WithdrawQueue contract, can retail investors continue to participate in the protocol deposit. However, during this period, deposits of less than or equal to 32 stETH will be rejected
The key to this vulnerability is that: If the bufferToFill
value of an ERC20 is equal to X, then when the amount of staking is less than or equal to X, it will be rejected. Any large investor or institution making a normal withdrawal (or an attacker maliciously withdraw to manipulating the value of bufferToFill
) will cause the value of X to become very large, which is very detrimental to retail investors.
Manual audit
Modify the deposit function of the OperatorDelegator contract. When tokenAmount=0, do not execute the revert statement, use the return 0;
statement instead.
function deposit( IERC20 token, uint256 tokenAmount ) external nonReentrant onlyRestakeManager returns (uint256 shares) { if (address(tokenStrategyMapping[token]) == address(0x0) || tokenAmount == 0) - revert InvalidZeroInput(); + return 0 // Move the tokens into this contract token.safeTransferFrom(msg.sender, address(this), tokenAmount); return _deposit(token, tokenAmount); }
Error
#0 - c4-judge
2024-05-20T05:02:17Z
alcueca marked the issue as satisfactory
#1 - c4-judge
2024-05-24T10:26:23Z
alcueca changed the severity to 2 (Med Risk)