Closed sherlock-admin2 closed 3 months ago
I believe the comments on that function are outdated. In the previous version of the codebase, there was a function to remove a reward token, but it was deleted before the Sherlock audit. Most probably that comment was referring to a reward token that was removed and could be recovered by the admins.
However, because now there's no way to remove reward tokens, those tokens cannot be recovered from the contract, and that is the intended design. It doesn't make any sense for the admins to recover some tokens from old rewards because maybe some users haven't claimed them yet so they'd lose them.
Hi @santipu03 we can not assume anything .
As the code comments clearly states meant for recovering tokens that are not actively being used as rewards or are not the staking token itself
, the code is indeed designed for what's in there and we should not assume that protocol will leave those tokens for other users . This functionality is intended for the time when the admin actually wants to recover tokens
without adding any extra edge case here 'users have not claimed... etc' .
if this was the concern , the protocol MUST NOT even have added this method.
Also, There should be no regards to either codebase has something before the audit . Because this is the scope , and the attack path is clear , i believe it's a valid issue.
That comment made sense when there was a function to remove reward tokens, and therefore to set the lastUpdateTime
at zero. Now that the function is not there, no reward tokens can be recovered, this is the protocol design.
Hi @santipu03 I respect your intuitive answers but as the fellow whitehat said ,
When talking about the severity of an issue, we should not talk about how likely it is to happen (some almost impossible examples that actually happened: the Luna crash, or USDC depeg).
The Sherlock rules also does not talk about the possibility, since it is impossible to quantify. The scenario described above is possible (however less likely ) and falls under "certain external conditions or specific states".
Causes a loss of funds but requires certain external conditions or specific states, or a loss is highly constrained.
The losses must exceed small, finite amount of funds, and any amount relevant based on the precision
or significance of the loss.
Owing to above reasons , i believe the issue is valid and i look forward to what the head of the judging thinks.
Your comment doesn't relate to this issue because in this case the scenario you describe will never be given. The two possible scenarios are the following:
recoverERC20
because there's nothing to recover. notifyUnseenReward
and those tokens will be distributed between all stakers, so it makes no sense for the admins to recover those tokens because they're getting distributed. There are no possible scenarios where the admins add a reward token, send tokens, and then they need to recover them. For this reason, this issue is invalid.
After my unbiased review , i conclude that the decision is valid. Thank you @santipu03 for putting so much explanation , really helping me learn where to focus more on finding valid bugs. I appreciate it.
0xreadyplayer1
high
Inactive Reward tokens can never be recovered : Funds will be stuck indefinitely
Summary
The
Lock.sol
holds an array of reward tokens that are used by the protocol to incentivize the stakers for locking their staking tokens. The rewards are standard ERC20 tokens. There is a method to add reward tokens to the lock system and that method isaddReward
which is owner only.While most rewards will be active , some of them might be in-active for longer periods of time i.e 2 years or 4 years
In that time , there is a special function to recover those in-active reward tokens from the Lock system
and that method is
recoverERC20
which is used to recover those in-active tokensif and only if they are
not active ( lastUpdatedTime= 0
) and the token to be recovered is not the staking token.However , there's a flaw in the code that assumes that when the token is in active for years , the
lastUpdatedTime
might beequal to zero however , there's no place in the code that sets the token to inactive state ( marking its
lastUpdatedTime= 0
)All the points that manipulate the
lastUpdatedTime
, they always set it tonon-zero block.timestamp > 0 because block.timestamp can never be zero
Vulnerability Detail
The issue exists in
Lock.recoverERC20
methodThe Natspec states the purpose of this method :
meant for recovering tokens that are not actively being used as rewards or are not the staking token itself.
The method is not intended to allow recovering tokens that are active , ensured by following line
it means if
lastUpdateTime
is0
then it will allow withdrawing it from Lockhowever ,if we look at all the places where
lastUpdateTime
is being setIf we check these assignments , its evident that
lastUpdateTime
will always be>0
becauseblock.timestamp is always >0
As
lastUpdateTime
will never be set to0
, reward tokens will never be taken out from Lock contractand tokens worth Billions of Dollars will get stuck in the contract that are not active.
PoC
Insert this into a new file inside tests
ExploitTest.t.sol
PoC output
Impact
Loss of funds to the protocol when reward tokens are not used as active rewards and tokens will be stuck.
Severity
As this issue causes
Definite loss of funds without (extensive) limitations of external conditions
, according to sherlock rules , it's a valid High severity issue.Code Snippet
https://github.com/sherlock-audit/2024-05-gamma-staking/blob/main/StakingV2/src/Lock.sol#L659
Tool used
Manual Review
Recommendation
Apply some threshold for marking a token as in-active like if 365 days are passed , and the token is not used as a reward token
that token can be recovered.
Another method can be to add a setter method for marking rewards tokens to be in-active and then withdrawing through
recoverERC20 method.