This analysis investigates a 42m attack on the GMX protocol. We provide detailed technical analysis of vulnerability and include work reproduction of attack scenarios for educational purposes in a forked environment.
Use the attack Mutual contract re -vulnerability Increases the detoured access control while the position increases. As a result, GLP token prices were operated at a higher price, allowing attackers to repay the tokens and extract profits from protocols.
Reproduction with Wake
- Replicate the repository
- GMX project dependency import:
$ npm i - Initialization Wake:
$ wake up - Receive and set ARBITRUM for URL from alchemy or other providers.
.envSimilar.env.example. - Execution:
$ wake test tests/test_attack_simple.py - compromise
print(tx.call_trace)To see the call tracking.
Root cause
The vulnerability comes from re -entry. Re -entry itself is simple, but its impact is important.
Core problem: GLP token price calculation globalShortAveragePrices Variable ShortsTracker. This dependence creates an exploited attack vector.
The vulnerability is a re -creation of mutual contracts. Many contracts have been related during the transaction. Each contract has a re -creation guard. However, the re -creation has already occurred after the termination of a specific contract.
Entry
The attack begins when the user increases the position.
- User call
createIncreaseOrderTo register an order - Order Keeper Bot calls
PositionManager.executeIncreaseOrderRun it - within
executeIncreaseOrder,,,ShortsTracker.updateGlobalShortDataCalled
ShortsTracker.updateGlobalShortData save globalShortAveragePrice In the case of tokens -average input price of all short positions. This value directly affects GLP token price calculations.
contract PositionManager
function executeIncreaseOrder(
address _account,
uint256 _orderIndex,
address payable _feeReceiver
) external onlyOrderKeeper
//...
IShortsTracker(shortsTracker).updateGlobalShortData(_account, collateralToken, indexToken, isLong, sizeDelta, markPrice, true);
ITimelock(timelock).enableLeverage(_vault); // isLeverageEnabled <- True
IOrderBook(orderBook).executeIncreaseOrder(_account, _orderIndex, _feeReceiver);
ITimelock(timelock).disableLeverage(_vault); // isLeverageEnabled <- False
_emitDecreasePositionReferral(_account, sizeDelta);
External currencies achieve this path Vault:
OrderBook.executeIncreaseOrderRouter.pluginIncreasePosition
that decreasePosition The flow follows a similar pattern.
that Vault.increasePosition The function checks it isLeverageEnabled Equivalent True Make sure the call occurs Timelock.enableLeverage and Timelock.disableLeverage. This inspection turned out to be insufficient.
contract Vault {
// function has no msg.sender check.
// Assumes caller transfers tokens or at least the caller is trusted.
function increasePosition(
address _account,
address _collateralToken,
address _indexToken,
uint256 _sizeDelta,
bool _isLong
) external override nonReentrant
_validate(isLeverageEnabled, 28); // this will be bypassed
_validateGasPrice();
_validateRouter(_account);
...
...
during Vault.decreasePositionThe contract transmits a collateral token for the closed location. If the mortgage token is Weth, the system withdrew the ETH and sent it to the user’s account. In particular, these WETH work occurs outside Vault contract.
The call flow proceeds as follows:
OrderBook.executeDecreaseOrderRouter.pluginDecreasePositionVault.decreasePosition- REENTRANTRANCYGUARD set
ENTERED VaultClose the location- Send Weth to Orderbook
- REENTRANTRANCYGUARD set
NOT_ENTERED
- REENTRANTRANCYGUARD set
OrderBookWithdraw the ETH- ETH is sent to the user
User.receiveTriggerVault.increasePosition(Abuse)- Reentrancyguard checks
NOT_ENTERED - REENTRANTRANCYGUARD has been set
ENTERED - The attack continues…
- Reentrancyguard checks
Re -creation guard Vault Start NOT_ENTEREDHowever, after this status is reset, a re -entry call occurs and bypassed protection.
Attack escalation
directly Vault.increasePosition Bypass call ShortsTracker.updateGlobalShortDatacause GlpManager.getAum Returns the expansion value and artificially increases the price of GLP tokens.
Attack order:
- It goes back through an open entry point
- To get GLP tokens, add fluidity
- call
increasePositionGLP token price is manipulated up - Remove liquidity at the expansion GLP token price
Operation details
Used by an attacker RewardRouterV2.mintAndStakeGlp because GLPManager.inPrivateMode It is activated by preventing direct calls GLPManager.addLiquidity.
The attacker uses a flash loan with a USDC to create a large WBTC short position.
summation
The attack was successful due to the fragmentary data responsibility for the contract. Important status information is divided ShortsTracker and VaultREENTRANTRANTRANCY Guard is inefficiently rendered. This vulnerability has made the attacker manipulates the price of GLP tokens through a carefully adjusted re -entry phone to enable millions of exploits.
