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— это общее количество выпущенных токенов GLP.AUM(управляемые активы) состоит из двух компонентов:- — это uPnL (т.е. нереализованная прибыль и убыток) всех коротких позиций.
- — это предоставленная ликвидность 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) Атакующий развернул контракт атаки, который используется как получатель активов во время исполнения ордеров. Контракт атаки содержал вредоносную функцию
fallback(). -
(Транз. 2) Атакующий создал ордер на увеличение WETH-long для контракта атаки в контракте
OrderBook.
-
(Транз. 3) Кипер исполнил ордер на увеличение (созданный на шаге 2), который открыл позицию WETH-long для контракта атаки. (Транз. 3).
-
(Транз. 4) Атакующий создал ордер на закрытие WETH-long для контракта атаки в контракте
OrderBook.
Фаза манипуляции
-
(Транз. 5) Кипер исполнил ордер на закрытие WETH-long контракта атаки (созданный на шаге 4) через путь orderbook-execution. Путь исполнения вызвал
_transferOutETH(), активировав вредоносную функциюfallback()в контракте атаки.Поскольку
fallback()был вызван во время «окна кредитного плеча», контракт атаки напрямую взаимодействовал с контрактом Vault для открытия позиции WBTC-short (черезincreasePosition()) без обновленияglobalShortAveragePrice, а затем создал ордер на закрытие WBTC-short.
-
(Транз. 6) Кипер исполнил ордер на закрытие WBTC-short (созданный на шаге 5) через путь router-execution. Исполнение также создало ордер на закрытие WETH-long через механизм fallback в контракте
PositionRouter.Поскольку позиция WBTC-short была открыта без обновления
globalShortAveragePrice, закрытие позиции с одновременным обновлением этой переменной вызвало неожиданное падениеglobalShortAveragePrice.
-
(Транз. 7-14) Атакующий повторил шаги 5-6 четыре раза. В результате
globalShortAveragePriceбыл искажен с 1.08e35 до 1.9e33.
Фаза реализации прибыли
-
(Транз. 15) Кипер исполнил ордер на закрытие WETH-long (созданный в Транз. 14) через путь orderbook-execution. Это исполнение активировало логику получения прибыли в контракте атаки из-за уязвимости реентрантности в контракте
OrderBook.
-
При вызове fallback контракт атаки сначала взял флэш-кредит (flash loan) на 7,538,567e18 USDC.
-
Контракт атаки вызвал
mintAndStakeGlp()для минтинга 4,129,578e18 GLP, используя 6,000,000e18 USDC. -
Контракт атаки вызвал
Vault.increasePosition()для открытия позиции WBTC-short на оставшиеся 1,538,567e18 USDC. Поскольку глобальные данные о шорт-позициях WBTC были крайне искажены, исполнение ордера значительно увеличило AUM, резко подняв цену GLP. -
Контракт атаки вызвал
unstakeAndRedeemGlp()для выкупа (redemption) GLP по завышенной цене за различные активы в контрактеVault. -
Контракт атаки вызвал
Vault.decreasePosition()для закрытия позиции WBTC-short. -
Контракт атаки повторил шаги 8.b-8.e четыре раза, чтобы полностью опустошить активы в контракте
Vault. -
Контракт атаки погасил флэш-кредит, получив прибыль почти в 42 миллиона долларов США.
-
Возврат средств
Благодаря переговорам с атакующим (Транз. 16-18), он в конечном итоге согласился на вознаграждение («баунти») в размере 10% и вернул оставшиеся украденные активы (Транз. 19-21).
Резюме
Этот инцидент включал многоэтапный эксплойт против GMX V1 в Arbitrum, который привел к ориентировочным убыткам в 42 миллиона долларов. Атакующий злоупотребил уязвимостью реентрантности в контракте 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, предотвратила множество взломов на сумму более 20 миллионов долларов и защитила криптовалюты на миллиарды долларов.
-
Официальный сайт: https://blocksec.com/
-
Официальный аккаунт в Twitter: https://twitter.com/BlockSecTeam



