Back to Blog

#4 Incidente GMX: Reentrada entre contratos elude una protección de cuatro años

Code Auditing
February 10, 2026
9 min read

El 9 de julio de 2025, la plataforma de perpetuos descentralizada GMX sufrió un exploit [1, 2] dirigido a su contrato V1 en la red Arbitrum, resultando en una pérdida de aproximadamente $42 millones. El atacante explotó una vulnerabilidad de reentrada entre contratos para manipular el precio de GLP, y luego utilizó el precio distorsionado para extraer activos subyacentes de los pools de liquidez de GMX V1.

Contexto

GMX V1 [3] es una plataforma de trading de perpetuos descentralizada desplegada en Arbitrum. Permite a los usuarios operar contratos perpetuos sobre múltiples activos criptográficos con apalancamiento de manera sin permisos y sin custodia. GMX V1 sigue un diseño de pool único multi-activo, donde la liquidez de todos los activos compatibles se agrega en un sistema de Vault unificado.

Gestión de Posiciones en GMX

La gestión de posiciones en GMX es un proceso de dos pasos. Concretamente, los usuarios crean órdenes de incremento/disminución interactuando con los contratos OrderBook o PositionRouter. Luego, la cuenta keeper autorizada ejecuta las órdenes de los usuarios, lo que actualiza los datos globales de posiciones cortas en el contrato ShortTracker y el estado correspondiente del contrato Vault. Las siguientes dos figuras demuestran los dos caminos de ejecución (el camino de ejecución-por-orderbook y el camino de ejecución-por-router) para gestionar posiciones en GMX. En este incidente, el atacante utilizó ambos caminos para manipular los datos globales de posiciones cortas y obtener ganancias.

El Camino de Ejecución-por-Orderbook

El Camino de Ejecución-por-Router

GMX Vault

El contrato Vault en GMX es responsable de gestionar las posiciones y activos de los usuarios (por ejemplo, finalizar las ganancias y pérdidas). Para evitar interacciones maliciosas, el contrato Vault solo es accesible cuando la "ventana de apalancamiento" está abierta (es decir, la variable isLeverageEnabled en el contrato Vault está configurada en true mediante la función Timelock.enableLeverage()). Esta validación garantiza que la ejecución de órdenes debe seguir los caminos fijos (ejecución-por-orderbook y ejecución-por-router).

GMX Short Tracker

El contrato ShortTracker es responsable de rastrear y actualizar los datos globales de posiciones cortas (por ejemplo, la variable globalShortAveragePrice) durante la ejecución de órdenes. De acuerdo con los caminos de ejecución-por-orderbook y ejecución-por-router, la función updateGlobalShortData() del contrato ShortTracker se invoca antes de ejecutar las órdenes de los usuarios para sincronizar los datos globales de posiciones cortas más recientes. Este paso garantiza la correcta realización de ganancias y pérdidas para las posiciones de los usuarios.

Disminución de Posición WETH

A diferencia de otras órdenes, disminuir la posición WETH mediante el camino de ejecución-por-orderbook invocará la función _transferOutETH() en el contrato OrderBook para retirar tokens WETH y transferir los tokens ETH nativos a los usuarios. Si el destinatario _receiver es un contrato, sendValue() podría activar la función fallback() del contrato, introduciendo una posible vulnerabilidad de reentrada. En este incidente, el atacante explotó repetidamente esta vulnerabilidad de reentrada para extraer ganancias significativas.

Token GLP

El token GLP (Proveedor de Liquidez de GMX) representa las participaciones unificadas para varios activos en el contrato Vault. Los usuarios pueden proporcionar y canjear activos mediante la acuñación y quema de GLPs.

El precio de GLP puede calcularse con las siguientes ecuaciones:

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} }

Donde:

  • GLPTotalSupply representa el suministro total de los tokens GLP.
  • AUM consta de los siguientes dos componentes.
    • ΔGlobalShort\Delta_{\text{GlobalShort}} representa el uPnL (es decir, las Ganancias y Pérdidas no realizadas) de todas las posiciones cortas.
    • AUMOther\text{AUM}_{\text{Other}} representa la liquidez proporcionada por el LP más el uPnL no realizado de todas las posiciones largas. Este término permaneció prácticamente sin cambios durante el incidente previo a la realización de ganancias.
  • AssetMarketPrice es el precio de mercado (en USD) del activo subyacente.
  • globalShortSize es la suma (en USD) de todas las posiciones cortas.
  • globalShortAveragePrice es el precio de entrada promedio de la posición corta global agregada.

En este incidente, el atacante sesgó globalShortAveragePrice para manipular el precio de GLP y extraer ganancias.

Análisis de la Vulnerabilidad

La causa raíz es una vulnerabilidad de reentrada entre contratos en el contrato OrderBook. Como se describe en el Contexto, disminuir posiciones WETH mediante el camino de ejecución-por-orderbook activa una llamada fallback de bajo nivel al receptor a través de _transferOutETH(). La función lleva un modificador nonReentrant, pero esta protección solo previene la reentrada dentro del propio contrato OrderBook, no las llamadas entre contratos al Vault.

En operación normal, increasePosition() del Vault solo puede ser llamado a través de PositionRouter y PositionManager, que invocan ShortTracker para actualizar globalShortAveragePrice antes de cada cambio de posición. El atacante creó órdenes con un contrato malicioso como receptor, y durante la llamada fallback, llamó directamente a increasePosition() en el Vault, evitando PositionRouter/PositionManager y la actualización asociada de ShortTracker. Al abrir y cerrar repetidamente posiciones cortas sin actualizar globalShortAveragePrice, el atacante sesgó progresivamente la variable. El valor distorsionado infló el precio de GLP, permitiendo al atacante extraer ganancias acuñando y canjeando GLPs.

Análisis del Ataque

El atacante lanzó una serie de transacciones manipulando los datos globales de posiciones cortas y realizando las ganancias. Concretamente, este incidente puede dividirse en tres fases: Preparación, Manipulación de Precio y Realización de Ganancias. Todas las transacciones relacionadas se enumeran en la siguiente tabla.

N.º de Tx Fase Descripción Transacción Hora (UTC)
1 Preparación Desplegar el contrato de ataque 0xa4ece5...8cd4c93f 09-Jul-2025 12:16:32 PM
2 Crear una orden de incremento de posición larga en WETH 0x0b8cd6...e90a4712 09-Jul-2025 12:22:28 PM
3 Ejecutar la orden de incremento de posición larga en WETH 0x28a000...7bf0beef 09-Jul-2025 12:23:23 PM
4 Crear una orden de disminución de posición larga en WETH 0x20abfe...decc49af 09-Jul-2025 12:24:56 PM
5 Manipulación de Precio 1 Ejecutar la orden de disminución de posición larga en WETH 0x1f00da...6a4a7353 09-Jul-2025 12:25:37 PM
6 Ejecutar la orden de disminución de posición corta en WBTC 0x222cda…c994464e 09-Jul-2025 12:25:43 PM
7 Manipulación de Precio 2 Igual que tx 5 0xc9a469...221293c2 09-Jul-2025 12:26:25 PM
8 Igual que tx 6 0x1cbf25...d853943a 09-Jul-2025 12:26:30 PM
9 Manipulación de Precio 3 Igual que tx 5 0xb58415...3b4cfb0b 09-Jul-2025 12:27:22 PM
10 Igual que tx 6 0x5a37ff...cb59c3b7 09-Jul-2025 12:27:28 PM
11 Manipulación de Precio 4 Igual que tx 5 0xff6fe6...377bf108 09-Jul-2025 12:28:13 PM
12 Igual que tx 6 0xbd65d6...e0187be6 09-Jul-2025 12:28:18 PM
13 Manipulación de Precio 5 Igual que tx 5 0x105273...19fcdec6 09-Jul-2025 12:29:12 PM
14 Igual que tx 6 0x0cdbac...84339fcc 09-Jul-2025 12:29:17 PM
15 Realización de Ganancias Realizar ganancias 0x03182d....a32626ef 09-Jul-2025 12:30:11 PM
16 Reembolso Enviar mensaje al atacante 0x92a39e...89547380 09-Jul-2025 02:04:19 PM
17 Respuesta al protocolo GMX 0x1d806c...919feac0 11-Jul-2025 06:29:00 AM
18 Respuesta al atacante 0x9c4ca9...39fa27fc 11-Jul-2025 07:42:17 AM
19 Reembolso 0x62b845...99211841 11-Jul-2025 08:04:34 AM
20 Reembolso 0x255d0a...9321b3 11-Jul-2025 08:08:27 AM
21 Reembolso 0xceafc3...a6313b22 11-Jul-2025 10:17:23 AM

Fase de Preparación

  1. (Tx 1) El atacante desplegó el contrato de ataque, que se utiliza como receptor de activos durante la ejecución de órdenes. El contrato de ataque contenía una función fallback() maliciosa.

  2. (Tx 2) El atacante creó una orden de incremento de posición larga en WETH para el contrato de ataque en el contrato OrderBook.

  3. (Tx 3) Un Keeper ejecutó la orden de incremento del atacante (creada en el paso 2), que abrió una posición larga en WETH para el contrato de ataque. (Tx 3).

  4. (Tx 4) El atacante creó una orden de disminución de posición larga en WETH para el contrato de ataque en el contrato OrderBook.

Fase de Manipulación

  1. (Tx 5) Un Keeper ejecutó la orden de disminución de posición larga en WETH del contrato de ataque (creada en el paso 4) mediante el camino de ejecución-por-orderbook. La ejecución invocó _transferOutETH(), activando la función fallback() maliciosa en el contrato de ataque.

    Dado que fallback() fue invocado durante la "ventana de apalancamiento", el contrato de ataque interactuó directamente con el contrato Vault para abrir una posición corta en WBTC (mediante increasePosition()) sin actualizar globalShortAveragePrice, y posteriormente creó una orden de disminución/cierre de posición corta en WBTC.

  2. (Tx 6) Un Keeper ejecutó la orden de disminución de posición corta en WBTC (creada en el paso 5) mediante el camino de ejecución-por-router. La ejecución también creó una orden de disminución de posición larga en WETH mediante el mecanismo fallback en el contrato PositionRouter.

    Dado que la posición corta en WBTC se abrió sin actualizar globalShortAveragePrice, cerrar la posición mientras se actualizaba la variable causó una caída inesperada en globalShortAveragePrice.

  3. (Tx 7-14) El atacante repitió los pasos 5-6 cuatro veces. Como resultado, globalShortAveragePrice fue sesgado de 1.08e35 a 1.9e33.

Fase de Realización de Ganancias

  1. (Tx 15) Un Keeper ejecutó la orden de disminución de posición larga en WETH (creada en Tx 14) mediante el camino de ejecución-por-orderbook. Esta ejecución activó la lógica de realización de ganancias en el contrato de ataque debido a la vulnerabilidad de reentrada en el contrato OrderBook.

    1. En la invocación fallback, el contrato de ataque primero tomó prestado un préstamo flash de 7,538,567e18 USDC.

    2. El contrato de ataque invocó mintAndStakeGlp() para acuñar 4,129,578e18 GLPs usando 6,000,000e18 USDC.

    3. El contrato de ataque invocó Vault.increasePosition() para abrir una posición corta en WBTC con los 1,538,567e18 USDC restantes. Debido a que los datos globales de posiciones cortas en WBTC estaban extremadamente sesgados, la ejecución de la orden amplificó significativamente el AUM, aumentando bruscamente el precio de GLP.

    4. El contrato de ataque invocó unstakeAndRedeemGlp() para canjear GLPs al precio amplificado por múltiples activos en el contrato Vault.

    5. El contrato de ataque invocó Vault.decreasePosition() para cerrar la posición corta en WBTC.

    6. El contrato de ataque repitió los pasos 8.b-8.e cuatro veces para drenar todos los activos en el contrato Vault.

    7. El contrato de ataque repagó el préstamo flash con una ganancia de casi $42 millones.

Reembolso

Mediante negociaciones con el atacante (Tx 16-18), el atacante finalmente aceptó una recompensa del 10% y devolvió los activos robados restantes (Tx 19-21).

Resumen

Este incidente involucró un exploit de múltiples fases contra GMX V1 en Arbitrum, resultando en una pérdida estimada de $42 millones. El atacante abusó de una vulnerabilidad de reentrada en el contrato OrderBook para sesgar la variable globalShortAveragePrice, inflando el precio de GLP. Aprovechando el precio manipulado, el atacante extrajo una cantidad significativa de activos.

Lecciones clave:

  • Reentrada entre contratos: Un modificador nonReentrant en un único contrato no previene la reentrada en otros contratos dentro del mismo sistema. Los mecanismos de control de acceso que dependen de una bandera temporal (por ejemplo, la "ventana de apalancamiento") pueden ser eludidos cuando ocurre una llamada externa antes de que la bandera sea restablecida.
  • Seguridad Operacional: Este atacante explotó el protocolo en tres fases distintas, ejecutando un total de 15 transacciones. Este incidente subraya la necesidad crítica de monitoreo en tiempo real, alertas oportunas y manuales de mitigación efectivos.

Referencias

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

Acerca de BlockSec

BlockSec es un proveedor integral de seguridad blockchain y cumplimiento cripto. Construimos productos y servicios que ayudan a los clientes a realizar auditorías de código (incluyendo contratos inteligentes, blockchain y billeteras), interceptar ataques en tiempo real, analizar incidentes, rastrear fondos ilícitos y cumplir con las obligaciones AML/CFT, a lo largo del ciclo de vida completo de protocolos y plataformas.

BlockSec ha publicado múltiples artículos de seguridad blockchain en conferencias prestigiosas, ha reportado varios ataques de día cero en aplicaciones DeFi, ha bloqueado múltiples hackeos para rescatar más de 20 millones de dólares y ha asegurado miles de millones en criptomonedas.

Best Security Auditor for Web3

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

BlockSec Audit