Steadefi

Steadefi
DeFiHardhatFoundryOracle
35,000 USDC
View results
Submission Details
Severity: medium
Invalid

Attacker can frontrun receiving native tokens

Summary

The vulnerability is related to the GMXVaul.sol contract's receive() function. It lacks proper safeguards, potentially enabling frontrunning attacks and allowing an attacker to manipulate the refund mechanism to gain undeserved native tokens.

receive() external payable {
if (msg.sender == _store.depositVault || msg.sender == _store.withdrawalVault) {
(bool success, ) = _store.refundee.call{value: address(this).balance}("");
require(success, "Transfer failed.");
}
}

Vulnerability Details

The vulnerability arises from the contract's receive() function, which refunds native tokens to a specific address under certain conditions. This function doesn't include proper safeguards to prevent potential frontrunning attacks, allowing an attacker to manipulate the refund mechanism to gain undeserved native tokens.

An attacker can frontrun and before receive() function call e.g. GMXDeposit.sol contract's deposit() function and become a refundee: self.refundee = payable(msg.sender) and gain all native tokens from fallback function.

function deposit(
GMXTypes.Store storage self,
GMXTypes.DepositParams memory dp,
bool isNative
) external {
// Sweep any tokenA/B in vault to the temporary trove for future compouding and to prevent
// it from being considered as part of depositor's assets
if (self.tokenA.balanceOf(address(this)) > 0) {
self.tokenA.safeTransfer(self.trove, self.tokenA.balanceOf(address(this)));
}
if (self.tokenB.balanceOf(address(this)) > 0) {
self.tokenB.safeTransfer(self.trove, self.tokenB.balanceOf(address(this)));
}
self.refundee = payable(msg.sender);
GMXTypes.HealthParams memory _hp;
_hp.equityBefore = GMXReader.equityValue(self);
_hp.lpAmtBefore = GMXReader.lpAmt(self);
_hp.debtRatioBefore = GMXReader.debtRatio(self);
_hp.deltaBefore = GMXReader.delta(self);
// Transfer assets from user to vault
if (isNative) {
GMXChecks.beforeNativeDepositChecks(self, dp);
self.WNT.deposit{ value: dp.amt }();
} else {
IERC20(dp.token).safeTransferFrom(msg.sender, address(this), dp.amt);
}
GMXTypes.DepositCache memory _dc;
_dc.user = payable(msg.sender);
if (dp.token == address(self.lpToken)) {
// If LP token deposited
_dc.depositValue = self.gmxOracle.getLpTokenValue(
address(self.lpToken),
address(self.tokenA),
address(self.tokenA),
address(self.tokenB),
false,
false
)
* dp.amt
/ SAFE_MULTIPLIER;
} else {
// If tokenA or tokenB deposited
_dc.depositValue = GMXReader.convertToUsdValue(
self,
address(dp.token),
dp.amt
);
}
_dc.depositParams = dp;
_dc.healthParams = _hp;
self.depositCache = _dc;
GMXChecks.beforeDepositChecks(self, _dc.depositValue);
self.status = GMXTypes.Status.Deposit;
self.vault.mintFee();
// Borrow assets and create deposit in GMX
(
uint256 _borrowTokenAAmt,
uint256 _borrowTokenBAmt
) = GMXManager.calcBorrow(self, _dc.depositValue);
_dc.borrowParams.borrowTokenAAmt = _borrowTokenAAmt;
_dc.borrowParams.borrowTokenBAmt = _borrowTokenBAmt;
GMXManager.borrow(self, _borrowTokenAAmt, _borrowTokenBAmt);
GMXTypes.AddLiquidityParams memory _alp;
_alp.tokenAAmt = self.tokenA.balanceOf(address(this));
_alp.tokenBAmt = self.tokenB.balanceOf(address(this));
_alp.minMarketTokenAmt = GMXManager.calcMinMarketSlippageAmt(
self,
_dc.depositValue,
dp.slippage
);
_alp.executionFee = dp.executionFee;
_dc.depositKey = GMXManager.addLiquidity(
self,
_alp
);
self.depositCache = _dc;
emit DepositCreated(
_dc.user,
_dc.depositParams.token,
_dc.depositParams.amt
);
}

Impact

An attacker could, in a frontrunning manner, deposit a small amount and become eligible for a refund. This could lead to an unintended transfer of native tokens to the attacker, effectively draining the contract's native token balance.

Tools Used

Manual review.

Recommendations

To mitigate this vulnerability, it is advised to implement additional checks and safeguards in the receive function. These checks should ensure that only valid refund requests are processed and that they are protected against manipulation or unauthorized access. Additionally, consider alternative design patterns to handle refunds securely.

Updates

Lead Judging Commences

hans Auditor
over 1 year ago
hans Lead Judge over 1 year ago
Submission Judgement Published
Invalidated
Reason: Other

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.