a user can consume all the gas
for the callback call from GMX after a Successful withdrawal which is 2000000
,or just cause it to revert causing the contract to stuck in withdraw
status.
the vulnerability lies in the function processWithdraw()
in the GMXWithdraw
library. consider this flaw :
the user create a withdraw request. with native token (i.e WETH). the vault then create a request withdraw in the GMX
protocol and set the status to withdraw
. (i'm Skipping this process because it's clear..)
the GMX
keeper then came and execute this request. then send the withdrawed token (WETH
),then calls the callback contract Specifically afterWithdrawalExecution()
function .
since the status is withdraw
, the function will call processWithdraw()
in the GMXVault
Which delegates the call to processWithdraw()
function in GMXwithdrawal
library.
the function then check if the status is withdraw
, if so it calls processWithdraw()
function in GMXProcessWithdraw
library,in a try,catch block.
if the call did not revert, and the token user withdrawing is nativeToken (aka WETH
). it Unwraped the token and send native to the user.
now here where the vulnerability lies this action is after the try and inside it's block. the user in this case can use all the remaining gas (it may be a contract that implement some logic when it receive eth) for the callback or just revert receiving ETH causing a revert without resseting the status to withdraw_failed
(since the revert happend inside the try block it will not get catched),and without emitting the event (so the keeper will never notice that). the withdraw from GMX
remain Successful even if the callback fail since it's in try,catch block see here.and in this case no one can rest the state since it's stuck on withdraw
status.waiting for GMX
to callback. which will never happen again .
if this was targeted by a user to specifically freeze the contract, he can just revert when the contract send him eth.
I don't know exactly how the keepers will behave in this situation.. but the only action allowed is to call processWithdraw
again since the status is withdraw
and in this case even if the keeper have a mechanism to detect this. he only able to call processWithdraw
again .This doesn't resolve the issue.because calling it again will revert for the same reason (the user revert) each time the keeper calls it.
in the same test contract for withdraw here create a simple wasteGas contract :
then add this function to the GMXWithdrawTest
contract :
also make sure to implement the correct MockExchangeRouter
contract , in the function executeWithdrawal()
to behave like real GMX
callback , in try,catch blocks. this is the change to do :
run test :
manual review
vs code
If the token is native send it in the processwithdrawa()
that is in try in GMXProcessWithdraw
library. so if it's failed it get catched. and the status change to withdraw_failed
an the keeper then can settel the vault.
also Restrict the gas
for this call, so user can't consume all the remaining gas for the callback
causing it to revert.
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.