Back to Blog

#4 GMXインシデント:クロスコントラクト再入稿が4年前のガードをバイパス

Code Auditing
February 10, 2026
9 min read

2025年7月9日、分散型パーペチュアルプラットフォームであるGMXは、Arbitrumネットワーク上のV1コントラクトを標的としたエクスプロイト(約4200万ドルの損失)が発生しました [1, 2]。攻撃者はクロスコントラクトのリエントランシー脆弱性を悪用してGLP価格を操作し、その歪んだ価格を用いてGMX V1の流動性プールから基盤資産を不正に引き出しました。

背景

GMX V1 [3]は、Arbitrum上に展開された分散型パーペチュアル取引プラットフォームです。ユーザーは、レバレッジをかけた複数の暗号資産のパーペチュアルコントラクトを、パーミッションレスかつノンカストディアルな方法で取引できます。GMX V1は、単一のマルチアセットプール設計を採用しており、サポートされているすべての資産の流動性が単一のVaultシステムに集約されています。

GMXにおけるポジション管理

GMXのポジション管理は2段階のプロセスです。具体的には、ユーザーはOrderBookまたはPositionRouterコントラクトと対話することで、増減注文を作成します。その後、認証されたキーパーアカウントがユーザーの注文を実行し、ShortTrackerコントラクトのグローバルショートデータとVaultコントラクトの対応する状態が更新されます。以下の2つの図は、GMXでポジションを管理するための2つの実行パス(orderbook-executionおよびrouter-executionパス)を示しています。このインシデントでは、攻撃者は両方のパスを使用してグローバルショートデータを操作し、利益を実現しました。

Orderbook-executionパス

Router-executionパス

GMX Vault

GMXのVaultコントラクトは、ユーザーのポジションと資産(例:損益の確定)の管理を担当します。悪意のあるインタラクションを回避するため、Vaultコントラクトは「レバレッジウィンドウ」が開いている場合(つまり、Vaultコントラクトの変数isLeverageEnabledTimelock.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トークン

GLP(GMX Liquidity Provider)トークンは、Vaultコントラクト内のさまざまな資産の統合されたシェアを表します。ユーザーは、GLPをミントおよびバーンすることによって資産を提供および償還できます。

GLP価格は、次の式で計算できます。

GLPPrice=AUMGLPTotalSupply\text{GLPPrice}= \frac{\text{AUM}}{\text{GLPTotalSupply}}

AUM=i=asset[0]assets(ΔGlobalShort[i]+AUMOther[i])\text{AUM} = \sum^{assets}_{i = asset[0]}(\Delta_{\text{GlobalShort[i]}} + \text{AUM}_{\text{Other[i]}})

ΔGlobalShort[i]=globalShortSize×(AssetMarketPriceglobalShortAveragePrice)globalShortAveragePrice\Delta_{\text{GlobalShort[i]}} = \frac{ \text{globalShortSize} \times (\text{AssetMarketPrice} - \text{globalShortAveragePrice} ) }{ \text{globalShortAveragePrice} }

ここで:

  • GLPTotalSupplyはGLPトークンの総供給量を示します。
  • AUMは次の2つのコンポーネントで構成されます。
    • ΔGlobalShort\Delta_{\text{GlobalShort}}は、すべてのショートポジションのuPnL(つまり、未実現損益)を表します。
    • AUMOther\text{AUM}_{\text{Other}}は、LPの提供流動性プラス、すべてのロングポジションの未実現uPnLを表します。この項は、利益実現前はインシデント全体を通してほぼ一定でした。
  • AssetMarketPriceは、基盤資産の市場価格(USD建て)です。
  • globalShortSizeは、すべてのショートポジションの合計(USD建て)です。
  • globalShortAveragePriceは、集約されたグローバルショートポジションの平均エントリー価格です。

このインシデントでは、攻撃者はglobalShortAveragePriceを操作してGLP価格を操作し、利益を引き出しました。

脆弱性分析

根本原因は、OrderBookコントラクトのクロスコントラクトリエントランシー脆弱性です。背景で説明したように、orderbook-executionパスを介してWETHポジションを減少させると、_transferOutETH()を介して受信者への低レベルのフォールバックコールがトリガーされます。この関数にはnonReentrant修飾子がありますが、このガードはOrderBookコントラクト自体のリエントランシーを防ぐだけで、Vaultへのクロスコントラクトコールは防ぎません。

通常の操作では、VaultincreasePosition()PositionRouterおよびPositionManagerを介してのみ呼び出すことができ、これらは各ポジション変更の前にShortTrackerを呼び出してglobalShortAveragePriceを更新します。攻撃者は、攻撃コントラクトを受信者として注文を作成し、フォールバックコールの間に、PositionRouter/PositionManagerおよび関連するShortTrackerの更新をバイパスして、直接VaultincreasePosition()を呼び出しました。globalShortAveragePriceを更新せずにショートポジションを繰り返し開閉することにより、攻撃者は徐々に変数を歪めました。歪んだ値はGLP価格を膨張させ、攻撃者がGLPをミントおよび償還することによって利益を引き出すことを可能にしました。

攻撃分析

攻撃者は、グローバルショートデータを操作し、利益を実現する一連のトランザクションを開始しました。具体的には、このインシデントは「準備」「価格操作」「利益実現」の3つのフェーズに分けることができます。関連するトランザクションは次の表にリストされています。

Tx No. フェーズ 説明 トランザクション 時間 (UTC)
1 準備 攻撃コントラクトをデプロイ 0xa4ece5...8cd4c93f 2025年7月9日 12:16:32
2 WETHロングポジション増加注文を作成 0x0b8cd6...e90a4712 2025年7月9日 12:22:28
3 WETHロングポジション増加注文を実行 0x28a000...7bf0beef 2025年7月9日 12:23:23
4 WETHロングポジション減少注文を作成 0x20abfe...decc49af 2025年7月9日 12:24:56
5 価格操作 1 WETHロングポジション減少注文を実行 0x1f00da...6a4a7353 2025年7月9日 12:25:37
6 WBTCショートポジション減少注文を実行 0x222cda…c994464e 2025年7月9日 12:25:43
7 価格操作 2 Tx 5 と同じ 0xc9a469...221293c2 2025年7月9日 12:26:25
8 Tx 6 と同じ 0x1cbf25...d853943a 2025年7月9日 12:26:30
9 価格操作 3 Tx 5 と同じ 0xb58415...3b4cfb0b 2025年7月9日 12:27:22
10 Tx 6 と同じ 0x5a37ff...cb59c3b7 2025年7月9日 12:27:28
11 価格操作 4 Tx 5 と同じ 0xff6fe6...377bf108 2025年7月9日 12:28:13
12 Tx 6 と同じ 0xbd65d6...e0187be6 2025年7月9日 12:28:18
13 価格操作 5 Tx 5 と同じ 0x105273...19fcdec6 2025年7月9日 12:29:12
14 Tx 6 と同じ 0x0cdbac...84339fcc 2025年7月9日 12:29:17
15 利益実現 利益を実現 0x03182d....a32626ef 2025年7月9日 12:30:11
16 返金 攻撃者にメッセージ 0x92a39e...89547380 2025年7月9日 14:04:19
17 GMXプロトコルへの応答 0x1d806c...919feac0 2025年7月11日 06:29:00
18 攻撃者への応答 0x9c4ca9...39fa27fc 2025年7月11日 07:42:17
19 返金 0x62b845...99211841 2025年7月11日 08:04:34
20 返金 0x255d0a...9321b3 2025年7月11日 08:08:27
21 返金 0xceafc3...a6313b22 2025年7月11日 10:17:23

準備フェーズ

  1. (Tx 1) 攻撃者は、注文実行中に資産受信者として使用される攻撃コントラクトをデプロイしました。攻撃コントラクトには悪意のあるfallback()関数が含まれていました。

  2. (Tx 2) 攻撃者は、OrderBookコントラクトで、攻撃コントラクト向けのWETHロングポジション増加注文を作成しました。

  3. (Tx 3) キーパーが(ステップ2で作成された)攻撃者の増加注文を実行し、攻撃コントラクトのWETHロングポジションを開設しました。(Tx 3)。

  4. (Tx 4) 攻撃者は、OrderBookコントラクトで、攻撃コントラクト向けのWETHロングポジション減少注文を作成しました。

操作フェーズ

  1. (Tx 5) キーパーが、orderbook-executionパスを介して、攻撃コントラクトの減少WETHロング注文(ステップ4で作成)を実行しました。この実行により、攻撃コントラクトの悪意のあるfallback()関数がトリガーされました。

    fallback()は「レバレッジウィンドウ」中に呼び出されたため、攻撃コントラクトはVaultコントラクトと直接対話してWBTCショートポジションを(increasePosition()を介して)開設し、その前にglobalShortAveragePriceを更新しませんでした。その後、WBTCショートポジションの減少/クローズ注文を作成しました。

  2. (Tx 6) キーパーが、router-executionパスを介して、WBTCショートポジション減少注文(ステップ5で作成)を実行しました。この実行により、PositionRouterコントラクトのフォールバックメカニズムを介して、減少WETHロング注文も作成されました。

    WBTCショートポジションはglobalShortAveragePriceを更新せずに開設されたため、この変数を更新しながらポジションをクローズすると、globalShortAveragePriceの予期せぬ低下が発生しました。

  3. (Tx 7-14) 攻撃者はステップ5-6を4回繰り返しました。その結果、globalShortAveragePrice1.08e35から1.9e33に歪みました。

利益実現フェーズ

  1. (Tx 15) キーパーが、orderbook-executionパスを介して、減少WETHロング注文(Tx 14で作成)を実行しました。この実行により、OrderBookコントラクトのリエントランシー脆弱性のため、攻撃コントラクトの利益実現ロジックがトリガーされました。

    1. フォールバック呼び出し中に、攻撃コントラクトはまず7,538,567e18 USDCのフラッシュローンを借りました。

    2. 攻撃コントラクトはmintAndStakeGlp()を呼び出し、6,000,000e18 USDCを使用して4,129,578e18 GLPをミントしました。

    3. 攻撃コントラクトはVault.increasePosition()を呼び出し、残りの1,538,567e18 USDCでWBTCショートポジションを開設しました。WBTCのグローバルショートデータが極端に歪んでいたため、注文実行はAUMを大幅に増幅させ、GLP価格を急上昇させました。

    4. 攻撃コントラクトはunstakeAndRedeemGlp()を呼び出し、Vaultコントラクト内の複数の資産の増幅された価格でGLPを償還しました。

    5. 攻撃コントラクトはVault.decreasePosition()を呼び出し、WBTCショートポジションをクローズしました。

    6. 攻撃コントラクトはステップ8.b-8.eを4回繰り返し、Vaultコントラクト内のすべての資産を吸い上げました。

    7. 攻撃コントラクトはフラッシュローンを返済し、約4200万ドルの利益を得ました。

返金

攻撃者との交渉(Tx 16-18)を経て、攻撃者は最終的に10%のバウンティを受け入れ、残りの盗まれた資産を返却しました(Tx 19-21)。

まとめ

このインシデントは、Arbitrum上のGMX V1に対する多段階エクスプロイトであり、推定4200万ドルの損失につながりました。攻撃者はOrderBookコントラクトのリエントランシー脆弱性を悪用してglobalShortAveragePrice変数を歪め、GLP価格をインフレさせました。操作された価格を利用して、攻撃者は相当量の資産を不正に引き出しました。

主な教訓:

  • クロスコントラクトリエントランシー: 単一コントラクト上のnonReentrant修飾子は、同じシステム内の他のコントラクトへのリエントランシーを防ぐものではありません。一時的なフラグ(例:「レバレッジウィンドウ」)に依存するアクセス制御メカニズムは、フラグがリセットされる前に外部コールが発生した場合にバイパスされる可能性があります。
  • 運用セキュリティ: この攻撃者は、3つの別個のフェーズでプロトコルを悪用し、合計15件のトランザクションを実行しました。このインシデントは、リアルタイム監視、迅速なアラート、および効果的な緩和プレイブックの極めて重要な必要性を強調しています。

参照

  1. https://x.com/GMX_IO/status/1942955807756165574
  2. https://x.com/GMX_IO/status/1943336664102756471
  3. GMX V1

BlockSecについて

BlockSecは、フルスタックのブロックチェーンセキュリティおよび暗号コンプライアンスプロバイダーです。当社は、コード監査(スマートコントラクト、ブロックチェーン、ウォレットを含む)、リアルタイムでの攻撃傍受、インシデント分析、不正資金追跡、およびAML/CFT義務の遵守を、プロトコルおよびプラットフォームのライフサイクル全体にわたって支援する製品とサービスを構築しています。

BlockSecは、著名なカンファレンスで複数のブロックチェーンセキュリティ論文を発表し、DeFiアプリケーションのゼロデイ攻撃を複数報告し、複数回ハッキングを阻止して2000万ドル以上を救済し、数十億ドルの暗号資産を確保しました。

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit