Open code423n4 opened 1 year ago
0xSorryNotSorry marked the issue as primary issue
Sidu28 marked the issue as disagree with severity
The description of lack of check here is accurate. There is zero actual impact; we think this is informational severity.
@Sidu28 if we assumed the Strategy not to be malicious, but to revert for some reason (e.g. lack of liquidity, smart contract bug, etc...)
Would you consider the finding as valid since the user cannot withdraw from all others due to having one withdraw, or is there some other reason why you believe the finding to be Informational?
GalloDaSballo changed the severity to 2 (Med Risk)
The Warden has shown how, any revert in a strategy (for example it being paused), will make queued withdrawals revert, even if the withdrawal should work for other strategies
This falls into the category of DOS and I believe is more appropriately judged as Medium
The Warden has shown how, any revert in a strategy (for example it being paused), will make queued withdrawals revert, even if the withdrawal should work for other strategies
This falls into the category of DOS and I believe is more appropriately judged as Medium
Even if a malicious strategy exists, there is no permanent delay here.
Users can mark the receiveAsTokens
input to completeQueuedWithdrawal
as false here https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L437-L442 and then the shares will be transferred to the 'withrdrawing' user here https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L795-L803 who can then queue a new withdrawal not containing the malicious strategy.
The description provided by the warden of the DelegationManager's behavior is inaccurate -- the funds do not end up 'stuck in the DelegationManager' -- they will be withdrawable as part of a new queued withdrawal which excludes the malicious strategy.
The Warden has shown how, any revert in a strategy (for example it being paused), will make queued withdrawals revert, even if the withdrawal should work for other strategies This falls into the category of DOS and I believe is more appropriately judged as Medium
Even if a malicious strategy exists, there is no permanent delay here.
Users can mark the
receiveAsTokens
input tocompleteQueuedWithdrawal
as false here https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L437-L442 and then the shares will be transferred to the 'withrdrawing' user here https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L795-L803 who can then queue a new withdrawal not containing the malicious strategy.The description provided by the warden of the DelegationManager's behavior is inaccurate -- the funds do not end up 'stuck in the DelegationManager' -- they will be withdrawable as part of a new queued withdrawal which excludes the malicious strategy.
Hey, https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L795-L803 does not do:
shares will be transferred to the 'withrdrawing' user
it simply increments internal accounting and delegated shares associated to user in the delegation contract. But these are not withdrawn without a queueWithdrawal
call
queue a new withdrawal not containing the malicious strategy.
a new queue can not reuse the already used balance as it was already accounted for in https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L345-L346
Maybe I am missing something of course. Regardless, awaiting judge feedback.
it simply increments internal accounting and delegated shares associated to user in the delegation contract. But these are not withdrawn without a
queueWithdrawal
call
Yes, this is what I would describe as transferring the shares, similar to how the 'transfer in' portion of an ERC20 transfer works. But this is a semantic argument; I agree with the substance of your description.
a new queue can not reuse the already used balance as it was already accounted for in https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L345-L346
Maybe I am missing something of course. Regardless, awaiting judge feedback.
I think perhaps you are missing that the internal _addShares
function calls the Delegation Manager as well? See here https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L646-L647
If the user marks receiveAsTokens
as false, then this line will be triggered https://github.com/code-423n4/2023-04-eigenlayer/blob/5e4872358cd2bda1936c29f460ece2308af4def6/src/contracts/core/StrategyManager.sol#L798
which calls into the internal _addShares
function and 're-adds' these delegated shares, so they can indeed be queued in a new withdrawal.
I can provide an example if it will help?
Basically, the 're-adding' reverses the accounting taken in the initial queueWithdrawal
action, to clarify. This then allows the accounting done in the queueWithdrawal
action to be performed once again, when a new withdrawal is queued.
@GalloDaSballo would appreciate you taking another look at this -- I am happy to provide additional context and/or an example, if desired.
Thank you @ChaoticWalrus for the tag, will check today
@ChaoticWalrus
It seems like the worst case scenario would be having to re-queue without the missing strategy which would require waiting for the withdrawal to unlock
So the question left to answer is whether an additional wait period is acceptable
NOTE: I edited this comment because as you pointed out the shares accounting is internal and doesn't trigger an interaction with the strategy
It seems like the worst case scenario would be having to re-queue without the missing strategy which would require waiting for the withdrawal to unlock
Yes, agreed this is the impact + worst-case scenario. The existing functionality was designed -- in part at least -- to address concerns around malicious Strategies.
So the question left to answer is whether an additional wait period is acceptable
I suppose so, yes. We have deemed this acceptable ourselves but I could see it being viewed differently. Regardless, the impact here is orders of magnitude less than the original claimed impact.
The Warden has shown how, due to the possibility of queueing of a withdrawal with multiple strategies, in the case in which one of the strategies stops working (reverts, paused, malicious), the withdrawal would be denied
As the sponsor said, in those scenarios, the withdrawer would have to perform a second withdrawal, which would have to be re-queued
Intuitively, a withdrawer that always withdraws a separate strategy would also never see their withdrawal denied (except for the malicious strategy)
As we can see there are plenty of side steps to the risk shown, however, the functionality of the function is denied, even if temporarily, leading to it not working as intended, and for this reason I believe Medium Severity to be the most appropriate
As shown above, the finding could be nofixed, however it seems to me like most users would want to plan around the scenarios described in this finding and its duplicates
GalloDaSballo marked the issue as satisfactory
GalloDaSballo marked the issue as selected for report
Lines of code
https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L786-L789
Vulnerability details
Impact
A malicious strategy can permanently DoS all currently pending withdrawals that contain it.
Vulnerability details
In order to withdrawal funds from the project a user has to:
queueWithdrawal
)completeQueuedWithdrawal(s)
)Queuing a withdrawal, via
queueWithdrawal
modifies all internal accounting to reflect this:https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L345-L346
https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L370
and saves the withdrawl hash in order to be used in
completeQueuedWithdrawal(s)
https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L400-L415
In other words, it is final (as there is no
cancelWithdrawl
mechanism implemented).When executing
completeQueuedWithdrawal(s)
, thewithdraw
function of the strategy is called (ifreceiveAsTokens
is set to true).https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L786-L789
In this case, a malicious strategy can always revert, blocking the user from retrieving his tokens.
If a user sets
receiveAsTokens
tofalse
, the other case, then the tokens will be added as shares to the delegation contracthttps://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L797-L800
https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L646-L647
This still poses a problem because the
increaseDelegatedShares
function's counterpart,decreaseDelegatedShares
is only callable by the strategy manager (so as for users to indirectly get theier rewards worth back via this workaround)https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/DelegationManager.sol#L168-L179
but in
StrategyManager
there are only 3 cases where this is called, none of which is beneficial for the user:recordOvercommittedBeaconChainETH
- slashes user rewards in certain conditionsqueueWithdrawal
- accounting side effects have already been done, will fail in_removeShares
slashShares
- slashes user rewards in certain conditionsIn other words, the workaround (of setting
receiveAsTokens
tofalse
incompleteQueuedWithdrawal(s)
) just leaves the funds accounted and stuck inDelegationManager
.In both cases all shares/tokens associated with other strategies in the pending withdrawal are permanently blocked.
Proof of Concept
https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L786-L789
Tools Used
Manual analysis
Recommended Mitigation Steps
Add a
cancelQueuedWithdrawl
function that will undo the accounting and cancel out any dependency on the malicious strategy. Although this solution may create a front-running opportunity for when their withdrawal will be slashed viaslashQueuedWithdrawal
, there may exist workarounds to this.Another possibility is to implement a similar mechanism to how
slashQueuedWithdrawal
treats malicious strategies: adding a list of strategy indices to skiphttps://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L532-L535 https://github.com/code-423n4/2023-04-eigenlayer/blob/main/src/contracts/core/StrategyManager.sol#L560-L566
Assessed type
DoS