该分析详细介绍了GMX协议遭受的4200万美元攻击事件,攻击源于跨合约重入漏洞,该漏洞绕过了增加仓位时的访问控制。攻击者利用此漏洞操纵GLP代币价格,使其价格升高,从而以操纵后的价格赎回代币,并从协议中提取利润。文章还提供了一个可重现攻击场景的工作示例,用于教育目的。
这个分析检查了针对 GMX 协议的 42M 攻击。 我们提供了漏洞的详细技术分析,并包括一个可在分叉环境中重现攻击场景的工作示例,以用于教育目的。
该攻击利用了 跨合约重入漏洞,该漏洞绕过了增加仓位期间的访问控制。这导致 GLP 代币价格被人为抬高,允许攻击者以被操纵的价格赎回代币,并从协议中提取利润。
$ npm i
$ wake up
.env
中设置它,类似于 .env.example
。$ wake test tests/test_attack_simple.py
print(tx.call_trace)
以查看调用跟踪。该漏洞源于重入问题。虽然重入本身很简单,但其影响是巨大的。
核心问题:GLP 代币价格计算依赖于 ShortsTracker
中的 globalShortAveragePrices
变量。这种依赖性创建了一个可利用的攻击向量。
该漏洞是跨合约重入。交易期间涉及多个合约。每个合约都有一个重入保护;但是,重入发生在某个特定合约已经退出之后。
有关跨合约重入的详细解释,请参见 本综合指南。
当用户增加他们的仓位时,攻击开始:
createIncreaseOrder
来注册订单PositionManager.executeIncreaseOrder
来执行它executeIncreaseOrder
中,调用 ShortsTracker.updateGlobalShortData
ShortsTracker.updateGlobalShortData
存储 token 的 globalShortAveragePrice
——所有空头头寸的平均入场价格。此值直接影响 GLP 代币价格的计算。
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);
}
}
外部调用遵循此路径到达 Vault
:
OrderBook.executeIncreaseOrder
Router.pluginIncreasePosition
Vault.increasePosition
decreasePosition
流程遵循类似的模式。
Vault.increasePosition
函数检查 isLeverageEnabled
是否等于 True
以验证调用发生在 Timelock.enableLeverage
和 Timelock.disableLeverage
之间。 事实证明,此检查是不够的。
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);
...
...
}
}
在 Vault.decreasePosition
期间,合约转移已平仓头寸的抵押代币。当抵押代币是 WETH 时,系统会提取 ETH 并将其转移到用户的帐户。值得注意的是,这些 WETH 操作发生在 Vault
合约之外。
呼叫流程如下:
OrderBook.executeDecreaseOrder
Router.pluginDecreasePosition
Vault.decreasePosition
ENTERED
Vault
平仓NOT_ENTERED
OrderBook
提取 ETHUser.receive
被触发
Vault.increasePosition
(利用)
NOT_ENTERED
ENTERED
Vault
中的重入保护最初为 NOT_ENTERED
,但重入调用发生在此状态已重置之后,绕过了保护。
直接的 Vault.increasePosition
调用绕过 ShortsTracker.updateGlobalShortData
,导致 GlpManager.getAum
返回膨胀的值并人为地增加 GLP 代币的价格。
攻击序列:
increasePosition
以向上操纵 GLP 代币价格攻击者使用 RewardRouterV2.mintAndStakeGlp
,因为 GLPManager.inPrivateMode
已启用,从而阻止直接调用 GLPManager.addLiquidity
。
攻击者在 fallback 函数中使用 USDC 的闪电贷创建了大量的 WBTC 空头头寸。
由于跨合约的数据责任分散,攻击得以成功。关键状态信息在 ShortsTracker
和 Vault
之间拆分,导致重入保护无效。这种架构漏洞允许攻击者通过精心策划的重入调用来操纵 GLP 代币价格,从而实现了数百万美元的利用。
- 原文链接: ackee.xyz/blog/gmx-hack-...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!