Back to Blog

Инцидент GMX №4: Межконтрактная реентрантность обходит защиту четырехлетней давности

Code Auditing
February 10, 2026
8 min read

9 июля 2025 года децентрализованная бессрочная торговая платформа GMX подверглась эксплойту [1, 2], направленному на их контракт V1 в сети Arbitrum, что привело к убыткам в размере приблизительно 42 миллионов долларов США. Атакующий воспользовался уязвимостью межконтрактного реентрантности (reentrancy) для манипуляции ценой GLP, а затем использовал искаженную цену для вывода базовых активов из пулов ликвидности GMX V1.

Предыстория

GMX V1 [3] — это децентрализованная платформа для торговли бессрочными контрактами, развернутая в сети Arbitrum. Она позволяет пользователям торговать бессрочными контрактами на несколько криптоактивов с использованием кредитного плеча без необходимости получения разрешений и без кастодиального хранения. GMX V1 использует архитектуру единого пула нескольких активов, где ликвидность всех поддерживаемых активов агрегируется в единую систему хранилищ (Vault).

Управление позициями в GMX

Управление позициями в GMX — это двухэтапный процесс. В частности, пользователи создают ордера на открытие/закрытие (increase/decrease) путем взаимодействия с контрактами OrderBook или PositionRouter. Затем авторизованный аккаунт-кипер исполняет ордера пользователей, что обновляет глобальные данные о шорт-позициях в контракте ShortTracker и соответствующее состояние контракта Vault. Следующие два рисунка демонстрируют два пути исполнения (пути orderbook-execution и router-execution) для управления позициями в GMX. В данном инциденте атакующий использовал оба пути для манипуляции глобальными данными о шорт-позициях и извлечения прибыли.

Путь исполнения через Orderbook (Orderbook-execution Path)

Путь исполнения через Router (Router-execution Path)

GMX Vault

Контракт Vault в GMX отвечает за управление позициями пользователей и активами (например, финализацию прибыли и убытков). Чтобы избежать злонамеренных взаимодействий, контракт Vault доступен только тогда, когда открыто «окно кредитного плеча» (т.е. переменная isLeverageEnabled в контракте Vault установлена в значение true через функцию Timelock.enableLeverage()). Эта проверка гарантирует, что исполнение ордеров должно следовать фиксированным путям (orderbook-execution и router-execution).

GMX Short Tracker

Контракт ShortTracker отвечает за отслеживание и обновление глобальных данных о шорт-позициях (например, переменная globalShortAveragePrice) во время исполнения ордеров. Согласно путям orderbook-execution и router-execution, перед исполнением ордеров пользователей вызывается функция updateGlobalShortData() контракта ShortTracker для синхронизации последних глобальных данных о шорт-позициях. Этот шаг обеспечивает корректную фиксацию прибыли и убытков по позициям пользователей.

Закрытие позиции WETH

В отличие от других ордеров, закрытие позиции WETH через путь orderbook-execution вызывает функцию _transferOutETH() в контракте OrderBook для вывода токенов WETH и перевода нативных токенов ETH пользователям. Если получатель _receiver является контрактом, sendValue() может вызвать функцию fallback() контракта, что создает потенциальную уязвимость типа реентрантность. В этом инциденте атакующий неоднократно использовал эту уязвимость для получения значительной прибыли.

Токен GLP

Токен GLP (GMX Liquidity Provider) представляет собой долю в едином пуле различных активов в контракте Vault. Пользователям разрешено предоставлять и выкупать активы путем минтинга (выпуска) и сжигания GLP.

Цена GLP рассчитывается по следующим формулам:

Цена GLP=AUMОбщий объем предложения GLP\text{Цена GLP}= \frac{\text{AUM}}{\text{Общий объем предложения GLP}}

AUM=i=asset[0]активы(ΔGlobalShort[i]+AUMПрочее[i])\text{AUM} = \sum^{активы}_{i = asset[0]}(\Delta_{\text{GlobalShort[i]}} + \text{AUM}_{\text{Прочее[i]}})

ΔGlobalShort[i]=globalShortSize×(Рыночная цена активаglobalShortAveragePrice)globalShortAveragePrice\Delta_{\text{GlobalShort[i]}} = \frac{ \text{globalShortSize} \times (\text{Рыночная цена актива} - \text{globalShortAveragePrice} ) }{ \text{globalShortAveragePrice} }

Где:

  • Общий объем предложения GLP — это общее количество выпущенных токенов GLP.
  • AUM (управляемые активы) состоит из двух компонентов:
    • ΔGlobalShort\Delta_{\text{GlobalShort}} — это uPnL (т.е. нереализованная прибыль и убыток) всех коротких позиций.
    • AUMПрочее\text{AUM}_{\text{Прочее}} — это предоставленная ликвидность LP плюс нереализованная прибыль/убыток всех длинных позиций. Этот показатель оставался практически неизменным на протяжении всего инцидента до момента фиксации прибыли.
  • Рыночная цена актива — это рыночная цена (в долларах США) базового актива.
  • globalShortSize — это общая сумма (в долларах США) всех коротких позиций.
  • globalShortAveragePrice — это средняя цена входа агрегированной глобальной короткой позиции.

В этом инциденте атакующий исказил globalShortAveragePrice, чтобы манипулировать ценой GLP и извлечь прибыль.

Анализ уязвимости

Первопричиной является уязвимость межконтрактной реентрантности в контракте OrderBook. Как описано в разделе «Предыстория», закрытие позиций WETH через путь orderbook-execution вызывает низкоуровневый вызов fallback получателю через _transferOutETH(). Функция содержит модификатор nonReentrant, но эта защита предотвращает реентрантность только внутри самого контракта OrderBook, а не межконтрактные вызовы к Vault.

При нормальной работе increasePosition() контракта Vault может быть вызван только через PositionRouter и PositionManager, которые вызывают ShortTracker для обновления globalShortAveragePrice перед каждым изменением позиции. Атакующий создал ордера с вредоносным контрактом в качестве получателя, и во время вызова fallback напрямую вызвал increasePosition() в Vault, минуя PositionRouter/PositionManager и соответствующее обновление в ShortTracker. Неоднократно открывая и закрывая короткие позиции без обновления globalShortAveragePrice, атакующий постепенно исказил значение этой переменной. Искаженное значение искусственно завышало цену GLP, позволяя атакующему извлекать прибыль путем минтинга и выкупа GLP.

Анализ атаки

Атакующий запустил серию транзакций, манипулируя глобальными данными о коротких позициях и реализуя прибыль. В частности, этот инцидент можно разделить на три фазы: Подготовка, Манипуляция ценой и Реализация прибыли. Все связанные транзакции перечислены в таблице ниже.

№ транз. Фаза Описание Транзакция Время (UTC)
1 Подготовка Развертывание контракта атаки 0xa4ece5...8cd4c93f 09 июля 2025, 12:16:32
2 Создание ордера на открытие WETH-long 0x0b8cd6...e90a4712 09 июля 2025, 12:22:28
3 Исполнение ордера на открытие WETH-long 0x28a000...7bf0beef 09 июля 2025, 12:23:23
4 Создание ордера на закрытие WETH-long 0x20abfe...decc49af 09 июля 2025, 12:24:56
5 Манипуляция 1 Исполнение закрытия WETH-long 0x1f00da...6a4a7353 09 июля 2025, 12:25:37
6 Исполнение закрытия WBTC-short 0x222cda…c994464e 09 июля 2025, 12:25:43
7 Манипуляция 2 Аналогично транз. 5 0xc9a469...221293c2 09 июля 2025, 12:26:25
8 Аналогично транз. 6 0x1cbf25...d853943a 09 июля 2025, 12:26:30
9 Манипуляция 3 Аналогично транз. 5 0xb58415...3b4cfb0b 09 июля 2025, 12:27:22
10 Аналогично транз. 6 0x5a37ff...cb59c3b7 09 июля 2025, 12:27:28
11 Манипуляция 4 Аналогично транз. 5 0xff6fe6...377bf108 09 июля 2025, 12:28:13
12 Аналогично транз. 6 0xbd65d6...e0187be6 09 июля 2025, 12:28:18
13 Манипуляция 5 Аналогично транз. 5 0x105273...19fcdec6 09 июля 2025, 12:29:12
14 Аналогично транз. 6 0x0cdbac...84339fcc 09 июля 2025, 12:29:17
15 Реализация прибыли Фиксация прибыли 0x03182d....a32626ef 09 июля 2025, 12:30:11
16 Возврат средств Сообщение атакующему 0x92a39e...89547380 09 июля 2025, 14:04:19
17 Ответ протоколу GMX 0x1d806c...919feac0 11 июля 2025, 06:29:00
18 Ответ атакующему 0x9c4ca9...39fa27fc 11 июля 2025, 07:42:17
19 Возврат 0x62b845...99211841 11 июля 2025, 08:04:34
20 Возврат 0x255d0a...9321b3 11 июля 2025, 08:08:27
21 Возврат 0xceafc3...a6313b22 11 июля 2025, 10:17:23

Фаза подготовки

  1. (Транз. 1) Атакующий развернул контракт атаки, который используется как получатель активов во время исполнения ордеров. Контракт атаки содержал вредоносную функцию fallback().

  2. (Транз. 2) Атакующий создал ордер на увеличение WETH-long для контракта атаки в контракте OrderBook.

  3. (Транз. 3) Кипер исполнил ордер на увеличение (созданный на шаге 2), который открыл позицию WETH-long для контракта атаки. (Транз. 3).

  4. (Транз. 4) Атакующий создал ордер на закрытие WETH-long для контракта атаки в контракте OrderBook.

Фаза манипуляции

  1. (Транз. 5) Кипер исполнил ордер на закрытие WETH-long контракта атаки (созданный на шаге 4) через путь orderbook-execution. Путь исполнения вызвал _transferOutETH(), активировав вредоносную функцию fallback() в контракте атаки.

    Поскольку fallback() был вызван во время «окна кредитного плеча», контракт атаки напрямую взаимодействовал с контрактом Vault для открытия позиции WBTC-short (через increasePosition()) без обновления globalShortAveragePrice, а затем создал ордер на закрытие WBTC-short.

  2. (Транз. 6) Кипер исполнил ордер на закрытие WBTC-short (созданный на шаге 5) через путь router-execution. Исполнение также создало ордер на закрытие WETH-long через механизм fallback в контракте PositionRouter.

    Поскольку позиция WBTC-short была открыта без обновления globalShortAveragePrice, закрытие позиции с одновременным обновлением этой переменной вызвало неожиданное падение globalShortAveragePrice.

  3. (Транз. 7-14) Атакующий повторил шаги 5-6 четыре раза. В результате globalShortAveragePrice был искажен с 1.08e35 до 1.9e33.

Фаза реализации прибыли

  1. (Транз. 15) Кипер исполнил ордер на закрытие WETH-long (созданный в Транз. 14) через путь orderbook-execution. Это исполнение активировало логику получения прибыли в контракте атаки из-за уязвимости реентрантности в контракте OrderBook.

    1. При вызове fallback контракт атаки сначала взял флэш-кредит (flash loan) на 7,538,567e18 USDC.

    2. Контракт атаки вызвал mintAndStakeGlp() для минтинга 4,129,578e18 GLP, используя 6,000,000e18 USDC.

    3. Контракт атаки вызвал Vault.increasePosition() для открытия позиции WBTC-short на оставшиеся 1,538,567e18 USDC. Поскольку глобальные данные о шорт-позициях WBTC были крайне искажены, исполнение ордера значительно увеличило AUM, резко подняв цену GLP.

    4. Контракт атаки вызвал unstakeAndRedeemGlp() для выкупа (redemption) GLP по завышенной цене за различные активы в контракте Vault.

    5. Контракт атаки вызвал Vault.decreasePosition() для закрытия позиции WBTC-short.

    6. Контракт атаки повторил шаги 8.b-8.e четыре раза, чтобы полностью опустошить активы в контракте Vault.

    7. Контракт атаки погасил флэш-кредит, получив прибыль почти в 42 миллиона долларов США.

Возврат средств

Благодаря переговорам с атакующим (Транз. 16-18), он в конечном итоге согласился на вознаграждение («баунти») в размере 10% и вернул оставшиеся украденные активы (Транз. 19-21).

Резюме

Этот инцидент включал многоэтапный эксплойт против GMX V1 в Arbitrum, который привел к ориентировочным убыткам в 42 миллиона долларов. Атакующий злоупотребил уязвимостью реентрантности в контракте OrderBook, чтобы исказить переменную globalShortAveragePrice, что привело к завышению цены GLP. Используя манипулированную цену, атакующий вывел значительное количество активов.

Ключевые уроки:

  • Межконтрактная реентрантность: Модификатор nonReentrant на отдельном контракте не предотвращает реентрантность в другие контракты внутри той же системы. Механизмы контроля доступа, которые полагаются на временный флаг (например, «окно кредитного плеча»), могут быть обойдены, если до сброса флага происходит внешний вызов.
  • Операционная безопасность: Атакующий эксплуатировал протокол в три четко различимые фазы, совершив в общей сложности 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, предотвратила множество взломов на сумму более 20 миллионов долларов и защитила криптовалюты на миллиарды долларов.

Best Security Auditor for Web3

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

BlockSec Audit