A malicious user can basically DOS the whole system on demand and leave it perpetually in a "Deposit" status.
For that to happen, let's look at the user flow of depositing when addLiquidity
to GMX fails and later we'll explain how we can force this.
The user flow would be:
User calls deposit()
on GMXVault, which sets Vault.Status = Deposit
Borrowing of assets and transfer of tokens happens, then addLiquidity
is called to GMX
If addLiquidity
fails then afterDepositCancellation
on GMXCallback is called, which calls processDepositCancellation
in GMXDeposit
This can be easily traced in the deposit sequence.
Let's take a look at the processDepositCancellation
in GMXDeposit:
As you can see if the deposit token is native, then we're going to refund the user's native token and check for success via these lines:
However, what a malicious user could do is deposit a native token through a smart contract that doesn't have a receive()
function, for example, making the call always revert. In that case, the processDepositCancellation
function can't be executed, and the status of the Vault can't be reset to Open, DOSing the system.
The user can maximize the chance of addLiquidity
failing by specifying a very low slippage amount in his deposit params which would be slippage = minSlippage(of the vault)
. minSlippage
here shouldn't be too high because otherwise, it'll be too restrictive for the users.
Given that slippage depends on liquidity, and liquidity depends on market conditions or it can also be influenced by flash loans, for example, I believe that the chance of a malicious user succeeding in his attempts to make addLiquidity
fail is very high.
Also, he can try numerous times with the MINIMUM_VALUE
deposit which is currently set as 9e16 in GMXChecks. 9e16 = 0.09ETH or AVAX, depending on which blockchain. 0.09 * 12 (price of AVAX) = ~1$
DOS of most of the functionality of the protocol. The status is set as "Deposit" when deposit()
is called, and being unable to processDepositCancellation
leaves the system in this status permanently.
Functions that are impacted:
Deposit will revert:
Withdraw will revert:
Rebalance will revert:
Compound will revert:
Manual review
You can wrap the call in a try/catch like at other places in the code. Or, you could just credit his balance in an internal mapping and let him claim his assets from another function claimFailedDeposit
.
Impact: High Likelihood: High An attacker can repeatedly force the protocol to get stuck in a not-open status. This can happen on both deposit, withdraw callback for both successful execution and failures. Will group all similar issues.
The contest is live. Earn rewards by submitting a finding.
This is your time to appeal against judgements on your submissions.
Appeals are being carefully reviewed by our judges.