2025年7月9日,去中心化永续合约平台GMX [1, 2] 的Arbitrum网络上的V1合约遭遇了攻击,损失约4200万美元。攻击者利用了跨合约重入漏洞操纵了GLP价格,然后利用扭曲的价格从GMX V1的流动性池中提取了底层资产。
背景
GMX V1 [3] 是部署在Arbitrum上的去中心化永续合约交易平台。它允许用户以无需许可和非托管的方式交易多种加密资产的杠杆永续合约。GMX V1采用单一多资产池设计,所有支持资产的流动性汇集到一个统一的Vault系统中。
GMX中的仓位管理
GMX中的仓位管理是一个两步过程。具体来说,用户通过与OrderBook或PositionRouter合约交互来创建增加/减少仓位订单。然后,授权的keeper账户执行用户的订单,这会更新ShortTracker合约中的全局空头数据以及Vault合约的相应状态。以下两张图展示了在GMX中管理仓位的两种执行路径(orderbook-execution和router-execution路径)。在此事件中,攻击者利用了这两种路径来操纵全局空头数据并实现利润。
Orderbook-execution 路径
Router-execution 路径
GMX Vault
GMX中的Vault合约负责管理用户的仓位和资产(例如,结算盈亏)。为了避免恶意交互,Vault合约只有在“杠杆窗口”打开时(即Vault合约中的isLeverageEnabled变量通过Timelock.enableLeverage()函数设置为true)才能访问。此验证强制订单执行必须遵循固定的路径(orderbook-execution和router-execution)。
GMX Short Tracker
ShortTracker合约负责在订单执行期间跟踪和更新全局空头数据(例如,globalShortAveragePrice变量)。根据orderbook-execution和router-execution路径,在执行用户订单之前会调用ShortTracker的updateGlobalShortData()函数来同步最新的全局空头数据。此步骤可确保用户仓位的正确盈亏结算。
减少WETH仓位
与其他订单不同,通过orderbook-execution路径减少WETH仓位将调用OrderBook合约中的_transferOutETH()函数来提取WETH代币并将原生ETH代币转账给用户。如果接收者_receiver是合约,sendValue()可能会触发该合约的fallback()函数,从而引入潜在的重入漏洞。在此事件中,攻击者反复利用此重入漏洞来提取巨额利润。
GLP Token
GLP(GMX流动性提供者)代币代表Vault合约中各种资产的统一份额。用户可以通过铸造和燃烧GLP来提供和赎回资产。
GLP价格的计算方法如下:
其中:
GLPTotalSupply代表GLP代币的总供应量。AUM由以下两部分组成。- 代表所有空头仓位的未实现盈亏(uPnL)。
- 代表LP提供的流动性加上所有多头仓位的未实现uPnL。在利润实现之前,此项在事件期间几乎保持不变。
AssetMarketPrice是标的资产的市场价格(以美元计)。globalShortSize是所有空头仓位的总和(以美元计)。globalShortAveragePrice是聚合的全局空头仓位的平均入场价。
在此事件中,攻击者通过操纵globalShortAveragePrice来操纵GLP价格并提取利润。
漏洞分析
根本原因在于OrderBook合约中的跨合约重入漏洞。如背景中所述,通过orderbook-execution路径减少WETH仓位会通过_transferOutETH()触发对接收者的低级fallback调用。该函数带有一个nonReentrant修饰符,但此保护仅防止OrderBook合约本身内部的重入,而不能防止对Vault的跨合约调用。
在正常操作下,Vault的increasePosition()只能通过PositionRouter和PositionManager调用,它们会在每次仓位变动前调用ShortTracker来更新globalShortAveragePrice。攻击者创建了以恶意合约作为接收者的订单,并在fallback调用期间,直接调用Vault上的increasePosition(),绕过了PositionRouter/PositionManager以及相关的ShortTracker更新。通过反复开仓和平仓而不更新globalShortAveragePrice,攻击者逐步扭曲了该变量。扭曲的值放大了GLP价格,使得攻击者可以通过铸造和赎回GLP来提取利润。
攻击分析
攻击者发起了一系列交易,操纵了全局空头数据并实现了利润。具体来说,此事件可分为三个阶段:准备、价格操纵和利润实现。所有相关交易列于下表中。
| 交易编号 | 阶段 | 描述 | 交易 | 时间(UTC) |
|---|---|---|---|---|
| 1 | 准备 | 部署攻击合约 | 0xa4ece5...8cd4c93f | 2025年7月9日 12:16:32 PM |
| 2 | 创建一个增加WETH多头订单 | 0x0b8cd6...e90a4712 | 2025年7月9日 12:22:28 PM | |
| 3 | 执行增加WETH多头订单 | 0x28a000...7bf0beef | 2025年7月9日 12:23:23 PM | |
| 4 | 创建一个减少WETH多头订单 | 0x20abfe...decc49af | 2025年7月9日 12:24:56 PM | |
| 5 | 价格操纵 1 | 执行减少WETH多头订单 | 0x1f00da...6a4a7353 | 2025年7月9日 12:25:37 PM |
| 6 | 执行减少WBTC空头订单 | 0x222cda…c994464e | 2025年7月9日 12:25:43 PM | |
| 7 | 价格操纵 2 | 同Tx 5 | 0xc9a469...221293c2 | 2025年7月9日 12:26:25 PM |
| 8 | 同Tx 6 | 0x1cbf25...d853943a | 2025年7月9日 12:26:30 PM | |
| 9 | 价格操纵 3 | 同Tx 5 | 0xb58415...3b4cfb0b | 2025年7月9日 12:27:22 PM |
| 10 | 同Tx 6 | 0x5a37ff...cb59c3b7 | 2025年7月9日 12:27:28 PM | |
| 11 | 价格操纵 4 | 同Tx 5 | 0xff6fe6...377bf108 | 2025年7月9日 12:28:13 PM |
| 12 | 同Tx 6 | 0xbd65d6...e0187be6 | 2025年7月9日 12:28:18 PM | |
| 13 | 价格操纵 5 | 同Tx 5 | 0x105273...19fcdec6 | 2025年7月9日 12:29:12 PM |
| 14 | 同Tx 6 | 0x0cdbac...84339fcc | 2025年7月9日 12:29:17 PM | |
| 15 | 利润实现 | 实现利润 | 0x03182d....a32626ef | 2025年7月9日 12:30:11 PM |
| 16 | 退款 | 给攻击者留言 | 0x92a39e...89547380 | 2025年7月9日 02:04:19 PM |
| 17 | 对GMX协议的响应 | 0x1d806c...919feac0 | 2025年7月11日 06:29:00 AM | |
| 18 | 对攻击者的响应 | 0x9c4ca9...39fa27fc | 2025年7月11日 07:42:17 AM | |
| 19 | 退款 | 0x62b845...99211841 | 2025年7月11日 08:04:34 AM | |
| 20 | 退款 | 0x255d0a...9321b3 | 2025年7月11日 08:08:27 AM | |
| 21 | 退款 | 0xceafc3...a6313b22 | 2025年7月11日 10:17:23 AM |
准备阶段
-
(Tx 1)攻击者部署了攻击合约,该合约在订单执行过程中用作资产接收者。攻击合约包含一个恶意的
fallback()函数。 -
(Tx 2)攻击者在
OrderBook合约中为攻击合约创建了一个增加WETH多头订单。
-
(Tx 3)一个Keeper执行了攻击者在步骤2中创建的增加订单,为攻击合约开了一个WETH多头仓位。(Tx 3)。
-
(Tx 4)攻击者在
OrderBook合约中为攻击合约创建了一个减少WETH多头订单。
操纵阶段
-
(Tx 5)一个Keeper通过orderbook-execution路径执行了攻击合约(在步骤4中创建)的减少WETH多头订单。执行路径调用了
_transferOutETH(),从而触发了攻击合约中的恶意fallback()函数。由于
fallback()在“杠杆窗口”期间被调用,攻击合约直接与Vault合约交互(通过increasePosition())开了一个WBTC空头仓位,而没有更新globalShortAveragePrice,随后创建了一个减少/关闭WBTC空头订单。
-
(Tx 6)一个Keeper通过router-execution路径执行了(在步骤5中创建的)减少WBTC空头订单。该执行还通过
PositionRouter合约中的fallback机制创建了一个减少WETH多头订单。由于WBTC空头仓位在未更新
globalShortAveragePrice的情况下被打开,关闭仓位时更新该变量导致globalShortAveragePrice意外下降。
-
(Tx 7-14)攻击者重复了步骤5-6四次。结果,
globalShortAveragePrice从1.08e35扭曲到1.9e33。
利润实现阶段
-
(Tx 15)一个Keeper通过orderbook-execution路径执行了(在Tx 14中创建的)减少WETH多头订单。此执行由于
OrderBook合约中的重入漏洞,触发了攻击合约中的利润实现逻辑。
-
在fallback调用中,攻击合约首先借入了7,538,567e18 USDC的闪贷。
-
攻击合约调用
mintAndStakeGlp(),使用6,000,000e18 USDC铸造了4,129,578e18 GLP。 -
攻击合约调用
Vault.increasePosition(),用剩余的1,538,567e18 USDC开设了一个WBTC空头仓位。由于WBTC的全局空头数据被极度扭曲,订单执行极大地放大了AUM,导致GLP价格急剧上涨。 -
攻击合约调用
unstakeAndRedeemGlp(),以放大的价格赎回了Vault合约中的多种资产的GLP。 -
攻击合约调用
Vault.decreasePosition()来关闭WBTC空头仓位。 -
攻击合约重复步骤8.b-8.e四次,耗尽了
Vault合约中的所有资产。 -
攻击合约偿还闪贷,利润近4200万美元。
-
退款
通过与攻击者协商(Tx 16-18),攻击者最终接受了10%的赏金,并退还了剩余的被盗资产(Tx 19-21)。
总结
此事件涉及针对Arbitrum上的GMX V1的多阶段攻击,导致估计损失4200万美元。攻击者滥用了OrderBook合约上的重入漏洞来扭曲globalShortAveragePrice变量,放大了GLP价格。通过利用操纵的价格,攻击者提取了大量资产。
关键教训:
- 跨合约重入:单个合约上的
nonReentrant修饰符并不能防止同一系统内其他合约的重入。依赖临时标志(例如“杠杆窗口”)的访问控制机制,在标志重置前发生外部调用时,可能会被绕过。 - 运营安全:此攻击者在三个独立的阶段利用了该协议,共执行了15笔交易。此事件凸显了实时监控、及时警报和有效缓解策略的至关重要性。
参考
- https://x.com/GMX_IO/status/1942955807756165574
- https://x.com/GMX_IO/status/1943336664102756471
- GMX V1
关于BlockSec
BlockSec是全栈区块链安全和加密合规提供商。我们构建产品和服务,帮助客户在协议和平台的整个生命周期内进行代码审计(包括智能合约、区块链和钱包)、实时拦截攻击、分析事件、追踪非法资金,并满足AML/CFT义务。
BlockSec曾在著名会议上发表过多篇区块链安全论文,报告了多起DeFi应用零日攻击,阻止了多起黑客攻击挽回超过2000万美元,并保障了数十亿美元的加密货币。
-
官方Twitter账号:https://twitter.com/BlockSecTeam
-
🔗 BlockSec 审计服务 : 提交请求



