-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Malicious liquidity provider can put pool into highly manipulatable state #154
Comments
0xRobocop marked the issue as primary issue |
0xRobocop marked the issue as sufficient quality report |
0xRobocop marked the issue as high quality report |
enthusiastmartin (sponsor) confirmed |
enthusiastmartin marked the issue as disagree with severity |
Although the check is missing,the issue is not high risk. Any limit that we have in our AMM are soft limits, meaning it is designed to protect mainly users, they dont have to be always respected. there is no evidence, that the state of pool would be exploitable. |
The warden identified how a security limit can be circumvented in some rare edge cases and how this could lead to a temporary DoS, Medium is appropriate here. |
OpenCoreCH marked the issue as selected for report |
Hi @OpenCoreCH
Here, the normal user can use the function Therefore, the only impact of this issue is allowing dust accounts to exist in the pool without any other impact, which should not be considered a medium severity issue. |
Hey @Frankcastleauditor, you are correct that the user could use 1. DOSRegarding the DOS, this issue still leads to a DOS on one of the functions of the protocol, which suffices medium, as per the severity guidelines Med requires "Assets not at direct risk, but the function of the protocol or its availability could be impacted". In this case the function of 2. Broken InvariantFrom the code, one can see that the intended invariant for the liquidity in a pool is High - A core invariant of the protocol can be broken for an extended duration.
Medium - A non-core invariant of the protocol can be broken for an extended duration or at scale, or an otherwise high-severity issue is reduced due to hypotheticals or external factors affecting likelihood. 3. ConclusionSo in total, the issue leads to 2 impacts, which both would be (at least) of medium severity:
|
Lines of code
https://github.com/code-423n4/2024-02-hydradx/blob/main/HydraDX-node/pallets/stableswap/src/lib.rs#L638
Vulnerability details
Bug Description
The StableSwap AMM of the HydraDx protocol implements safeguards against low liquidity so that too high price fluctuations are prevented, and manipulating the price becomes harder. These safeguards are enforced based on the
MinPoolLiquidity
which is a constant that describes the minimum liquidity that should be in a pool. Additionally, a pool is allowed to have a liquidity of 0, which would occur in the case of the creation of the pool, or by users withdrawing all their liquidity. This could also be defined as an invariant.When a user wants to withdraw his liquidity, he can use either the
remove_liquidity_one_asset()
function or thewithdraw_asset_amount()
function.remove_liquidity_one_asset()
To ensure holding the invariant 2 checks are implemented in the
remove_liquidity_one_asset()
function.The first check checks if the user either leaves more than
MinPoolLiquidity
shares in the pool or withdraws all his shares.The second check checks if the total liquidity in the pool would fall below the intended amount of shares.
These 2 checks work perfectly at holding the invariant at all times.
withdraw_asset_amount()
Unfortunately the second function for withdrawing liquidity
withdraw_asset_amount()
omits one of the checks. The function only checks if the user either withdraws all his shares or leaves more than theMinPoolLiquidity
shares.One might state now that this could never break the invariant, as if every user's shares are either more than$totalPoolIssuance(poolId) >= MinPoolLiquidity$ .
MinPoolLiquidity
or zero, the total liquidity can never fall belowMinPoolLiquidity
without being 0. Unfortunately, this approach forgets that users can transfer their shares to other addresses. This allows a user to transfer an amount as low as 1 share to another address, and then withdraw all his shares. As the check would only ensure that he is withdrawing all his shares it would pass. If he was the only liquidity provider, there now would only be 1 share of liquidity left in the pool breaking the invariant ofImpact
The issue allows a user to break the invariant about the
MinPoolLiquidity
and either push the pool into a state where it can easily be manipulated, or prevent other users from withdrawing their shares.Proof of Concept
There are 2 ways how this could be exploited:
1. Breaking the invariant and letting the pool Liquidity fall below
MinPoolLiquidity
A malicious LP could abuse this functionality to push the pool into a state where its total liquidity is below
MinPoolLiquidity
, and could be as low as 1 share, allowing for easy price manipulation. To achieve this the attacker would do the following:MinPoolLiquidty
using one of the functionswithdraw_asset_amount()
2. DOSing withdrawing through
remove_liquidity_one_asset
for othersLet's consider a case where there are only 2 liquidity providers and one of them is malicious and wants to prevent the other from withdrawing through
remove_liquidity_one_asset()
. This could for example be the case if the other is a smart contract, that can only withdraw through this function.MinPoolLiquidty
withdraw_asset_amount()
remove_liquidity_one_asset()
MinPoolLiquidty
Tools Used
Manual Review
Recommended Mitigation Steps
The issue can be mitigated by also adding a check for the total pool liquidity to
withdraw_asset_amount()
Assessed type
Other
The text was updated successfully, but these errors were encountered: