Closed sherlock-admin2 closed 1 month ago
How is this issue different from Protocol owner removing a oracle for a pool asset
after pool has been initialized?
Two things:
The RiskEngine.requestLtvUpdate
function sets the validAfter
to block.timestamp
if this is first time LTV is being set for the pool
.
if (ltvFor[poolId][asset] == 0) ltvUpdate = LtvUpdate({ ltv: ltv, validAfter: block.timestamp });
So there is no delay in this case. When the LTV is first being set, i.e pool has never been used before this, then acceptLTV
can be called immediately. And if LTV is not set then pool cannot be used. There's no impact until LTV is set atleast once.
Now the other case:
LTV is set and pool is being used. Multiple users have borrowed from the pool. All operations are working correctly.
Consider the following two exploit scenarios: First:
Impact: All pools that have this asset have stopped working.
Second scenario (mentioned in the issue):
requestLtvUpdate
.acceptLTV
and LTV has been updated.Impact: All pools that have this asset have stopped working. The impact happens immediately after the 2nd step.
As the issue mentions the root cause to be in acceptLtv
and acceptLtv
does not be considered for the impact.
The issue described is not cause for the impact shown. Impact comes from Protocol owner removing oracle for an asset. It does not matter if this was done in the middle of Ltv update.
Also Protocol owner decided to not support certain asset and owner has removed oracle for the asset.
is not a valid issue.
Escalate.
The main argument on why the following issue should be invalid in the above comment. As a summary issue relay on admin performing action, which would anyways brick the protocol usage. Such vulnerabilities are considered invalid regarding Sherlock docs.
Escalate.
The main argument on why the following issue should be invalid in the above comment. As a summary issue relay on admin performing action, which would anyways brick the protocol usage. Such vulnerabilities are considered invalid regarding Sherlock docs.
You've created a valid escalation!
To remove the escalation from consideration: Delete your comment.
You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final.
Protocol owner decided to not support certain asset and owner has removed oracle for the asset is not a valid issue.
The problem isn't about the admin deciding to remove an asset or not. The issue is how the LTV update should work in this case.
Now, to explain further regarding both of your exploit scenarios:
Let’s say the pool owner sets the LTV for the first time:
ltvFor[10][USDC] == 0
, the update happens immediately.Later on, they change the LTV again:
ltvFor[10][DAI] == 0
, so it updates right away.But now, the pool owner wants to go back to USDC:
ltvFor[10][USDC] != 0
, so they need to wait 72 hours.acceptLtvUpdate
, and since there’s no oracle for USDC, the pool breaks. The pool, which was fine with DAI, is now down while this could be completely mitigated by checking the oracle status.As you can see, it's not the Admin causing the DoS on the pool. What actually causes the pool to be inaccessible for 72 hours is the failure to check the oracle status when updating the LTV.
Your statement about this being an admin action is correct only when the admin removes an asset, which results in an immediate DoS for the pools actively using that asset. However, this report points out that the protocol isn't preventing LTV updates to an asset that has been removed.
I agree with the escalation.
This issue fits Sherlock's call validation rule, which is that admin actions can create an issue:
An admin action can break certain assumptions about the functioning of the code.
The root of the issue is that the admin can remove an Oracle for an asset after an LTV update request has been made but before it has been accepted, which fits the above rule.
Planning to accept the escalation and invalidate the issue.
hey @cvetanovv, thanks for the feedback!
i'd like to kindly ask you to reconsider the issue. as i pointed out, there are two distinct scenarios at play here:
Admin removes the oracle for an asset, which would indeed result in the pools utilizing that asset experiencing a DoS. this scenario could fall under the rule of admin action affecting the protocol’s behavior.
After the oracle is removed, logically, pool owners should not be allowed to update the LTV for the asset without an oracle. this is where the issue arises. allowing pool owners to proceed with the LTV update for an asset that no longer has a valid oracle is not simply an admin action. it's a lack of validation within the contract logic itself, which leads to the vulnerability.
i believe the second scenario falls outside the scope of admin action and highlights a missing validation step that should be addressed to prevent unintended DoS.
The second scenario won't happen if the first one isn't happening.
The admin removes Oracle, which causes issues in the code, and enters the Sherlock admin rule:
An admin action can break certain assumptions about the functioning of the code.
If the first action is not present, we have no issue.
My decision to accept the escalation remains.
hey @cvetanovv
using the logic that "the second scenario won’t happen if the first one doesn’t," we could invalidate a lot of vulnerabilities. almost every operation in a protocol stems from an admin action. by that reasoning, any vulnerability could be dismissed since the protocol’s deployment is also an admin action.
the real issue here is that not checking the oracle status when updating the LTV is a logic flaw, not an admin action. it’s this flaw that allows the DoS to happen, not the admin’s behavior. this distinction is important.
The escalation seems valid to me. It should be a low/info.
While this could be added as a redundant check, there is no strong impact-likelihood basis for this to be a medium. It should be a low due to low likelihood + dependence on admin errors.
hey @ruvaag thanks for sharing your feedback
regarding your first statement about the "redundant check": obviously, it's not a redundant check, but even if it were, the first one would be redundant because with both checks there, the absence of the first one won’t DoS the pool, the second one will.
i disagree with the "no-strong-impact" comment. i mean, if a Pool getting DoSed isn't a big deal, what is?
This issue is invalid because it depends on some actions of the admin.
Once the pool is initialized, the admin does not need to update the oracle. Even if he does and some functionality is broken, this is not valid according to Sherlock's rules:
Admin Input/call validation:
- An admin action can break certain assumptions about the functioning of the code. Example: Pausing a collateral causes some users to be unfairly liquidated or any other action causing loss of funds. This is not considered a valid issue.
My decision to accept the escalation and invalidate the issue remains.
@cvetanovv
Even if he does and some functionality is broken, this is not valid
sir, if the admin removes an asset (oracle), pools using that asset will break, and yeah, that's totally an admin action. but that’s not what this report is about. the issue here is the code allowing pool owners to update the LTV to an asset that shouldn’t be supported. so how can we call this an admin action?
it depends on some actions of the admin
by that logic, this finding would be invalid too because it happens due to an admin removing an asset while positions can still use it. but it's considered valid because the issue isn't the admin's action, it’s that the positions can still use an asset that shouldn’t be supported, even though the issue stems from an admin action.
@yashar0x
Even if we don't consider the admin rule, there is a getOracleFor()
function for anyone to check if an asset has an oracle.
function getOracleFor(address asset) public view returns (address) {
address oracle = oracleFor[asset];
if (oracle == address(0)) revert RiskEngine_NoOracleFound(asset);
return oracle;
}
The pool owner can call the function before calling acceptLtvUpdat()
to check the Oracle. If he doesn't, it is his mistake.
My decision to accept the escalation remains.
@cvetanovv
there is a
getOracleFor()
function for anyone to check if an asset has an oracle. The pool owner can call the function before callingacceptLtvUpdat()
to check the Oracle. If he doesn't, it is his mistake.
sir, we can’t justify skipping the check in the main function just because there’s a view
function to check the oracle status, especially since it’s not called within acceptLtvUpdate
. this seems more like a front-end check.
if the issue could be mitigated in this way and it was intended to be, it should have been included in the docs or in the known issues.
Result: Invalid Unique
@WangSecurity @Evert0x @cvetanovv
is it really okay to close the issue when we’re still discussing it? the issue is valid, and none of @cvetanovv’s points were accurate. my last comment hasn’t even been answered, but the issue got closed :| how?
@yashar0x I have written four times about how the escalation will end. There were three weeks for discussion. Since you think the escalation is incorrect, what I wrote is also invalid, and the comment of the sponsor is invalid, I'll give Evert or Wang to review the issue.
hey @cvetanovv
you think the escalation is incorrect
yep, i'm sure the escalation is incorrect, and i’ve addressed all the points raised by the escalator here
what I wrote is also invalid
i’ve responded to all your comments about the validity of the finding, but each time you bring up a new challenge, which is fine. i believe your main argument is that it's an admin action, but you haven’t clarified why this finding isn’t considered an admin action, but this issue is. i explained my view here.
and the comment of the sponsor is invalid
the sponsor originally confirmed the finding as medium, then after the escalation, he changed his mind. even then, he didn’t deny the existence of the bug, he just said it’s low, and i replied to that here.
i believe i’ve provided all the necessary info for your concerns but still haven’t gotten a justified answer.
it would be great if @WangSecurity and @Evert0x could take a look at the report and the whole discussion.
The escalation was resolved because @cvetanovv has explained his decision several times without changing it from the beginning. Hence, the discussion has become unnecessarily long with repeating similar arguments. @cvetanovv is correct that the Admin action rule applies here, and the issue should be invalid based on it. If you need, I could make a more detailed answer, but it would be repeating the same point as above, so there's no point in it.
hey @WangSecurity,
thanks a lot for the feedback! i think i answered all the doubts, but the issue still got closed. pretty sure it was unfair since i gave all the evidence showing it wasn’t an admin action. feels like @cvetanovv would’ve invalidated it anyway, so i guess there wasn’t much point in dragging it out. just wish it had happened sooner so we didn’t waste each other’s time. no need for more detail, just a bit disappointed. thanks again for checking it out, G!
@cvetanovv was indeed correct here, and it is my honest opinion that this rule has to apply here. The escalation was resolved because the discussion started to repeat the same arguments without changing the decision.
The protocol team fixed this issue in the following PRs/commits: https://github.com/sentimentxyz/protocol-v2/pull/311
The protocol team fixed this issue in the following PRs/commits: sentimentxyz/protocol-v2#311
it hurts :\
Yashar
Medium
Lack of oracle validation in
acceptLtvUpdate
can result in a DoS for the Pool-Asset pairSummary
The
RiskEngine.sol
allows pool owners to request LTV updates with a 72-hour timelock. However, while therequestLtvUpdate
function checks for a valid oracle, theacceptLtvUpdate
function does not. This could lead to a situation where an LTV update is accepted after the oracle has been removed or invalidated, resulting in DoS for the Pool-Asset pair.Vulnerability Detail
Pool owners can update LTV parameters using the
requestLtvUpdate
function, which employs a 72-hour timelock before the LTV change takes effect. During the request phase, the function ensures a valid oracle is set for the asset:After the timelock, the pool owner can accept this request via the
acceptLtvUpdate
function. However, given the 72-hour delay, there is a possibility that the protocol's admin could remove or change the oracle for the asset. TheacceptLtvUpdate
function does not re-check the oracle's validity before updating the LTV:If the LTV is updated for an asset without an oracle, the
getAssetValue
function, which fetches the asset's price from the oracle, will always revert, resulting in a DoS for the given Pool-Asset pair.Impact
If the LTV is updated for an asset without an oracle, it will cause a DoS for the affected Pool-Asset pair, as any attempts to fetch the asset's value will revert.
Code Snippet
https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/main/protocol-v2/src/RiskEngine.sol#L167-L187 https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/main/protocol-v2/src/RiskEngine.sol#L190-L210 https://github.com/sherlock-audit/2024-08-sentiment-v2/blob/main/protocol-v2/src/RiskModule.sol#L183-L187
Tool used
Manual Review
Recommendation
Re-check the validity of the oracle for the asset upon accepting the ltv update: