Estos 200 ETH consistían en cuatro lotes de 50 ETH que el contrato había ganado en subastas de MakerDAO con pujas nulas durante las liquidaciones del «jueves negro» de marzo de 2020, pero que nunca había reclamado finalmente.
Una de las funciones desprotegidas del contrato antiguo no verificaba quién la invocaba. Esto permitió al atacante otorgar a un contrato bajo su control el control total sobre la cuenta de este contrato en el sistema de MakerDAO. En una sola transacción, el atacante liquidó las subastas antiguas y retiró la garantía que se mantenía allí.
Transacciones clave (horas en UTC)
| Hora | TXID | Descripción |
|---|---|---|
| 2026-10-06 05:57:23 | 0xd4c0b18a058e7f0a12855f30174f4cb1e973c15c886063deab97e1a21f048ac6 | Retiro de 0.1 ETH desde Tornado Cash. Este fue el único financiamiento que recibió esta cuenta. |
| 2026-10-06 06:12:11 | 0x4a587af4213cc345c4c109fbf5fec46f9643183f91a9edf60305380f31ad1167 | Despliegue del contrato de ataque utilizado para explotar la vulnerabilidad. |
| 2026-10-06 06:13:11 | 0xbb6940f7c2a1e68cafbae7bb9b94d09af9af06ec3a114f6996f2cab993f3a88c | La transacción de ataque. El contrato de ataque obtuvo permisos delegados a través de la función del keeper 0x8804d1de, liquidó las subastas 1457-1460 y envió 200 ETH a la cuenta de propiedad externa (EOA) del atacante. |
| 2026-10-06 06:19:35 | 0xb8ecff9c129d8d9331c85558eaeddff29a38c4c16b71a40986acbf8f6febefeb | El primero de nueve depósitos de 10 ETH en el router de Tornado Cash. |
Direcciones clave
| Dirección | Rol |
|---|---|
| 0x01EB957E5C7DcDDD60F3C875956cCc6fb9BdA5FA | La cuenta de propiedad externa (EOA) del atacante. |
| 0xEc997d2aD033277913d6002277353368E8321dcF | El contrato de ataque. |
| 0xf09a13072Ed939B79Bc25B66AA3a836ea6DCC170 | El módulo adaptador malicioso, que aún conserva permisos delegados sobre la cuenta de la víctima en Vat. |
| 0x9c05a05893Ada984FC20D0DA0c046De5Cc0e8273 | El contrato vulnerable (keeper de subastas antiguo, AdminUpgradeabilityProxy). |
| 0x68399ed8aa33C5b43F863EE6782de492006A5546 | La implementación del contrato vulnerable (código fuente no verificado). |
| 0xaDC374E77b89Af0B8B39F7c47E8e0A37B6DaF073 | El propietario y operador del keeper en marzo de 2020. |
| 0xd8a04F5412223F513DC55F839574430f5EC15531 | MakerDAO ETH-A Flipper, un mecanismo de subasta retirado de servicio para la venta de garantías. |
| 0x35D1b3F3D7966A1DFe207aa4514C12a259A0492B | MakerDAO Vat, el módulo contable principal. |
| 0x2F0b23f53734252Bda2277357e97e1517d6B042A | MakerDAO ETH-A GemJoin, el punto de salida del activo de garantía. |
| 0xd90e2f925DA726b50C4Ed8D0Fb90Ad053324F31b | El router de Tornado Cash, la dirección que recibió los fondos robados. |
| 0x12D66f87A04A9E220743712cE6d9bB1B5616B8Fc | El pool de 0.1 ETH de Tornado Cash, la fuente del financiamiento inicial del atacante. |
Cómo se desarrolló el ataque
Despliegue del módulo
El exploit comenzó cuando el contrato de ataque creó un módulo adaptador en 0xf09a13072Ed939B79Bc25B66AA3a836ea6DCC170. Al crearse, el módulo recibió los siguientes parámetros: el Flipper, Vat, ETH-A GemJoin, el tipo de garantía (ilk), WETH, una dirección de pago y cuatro ID de subasta.
Entrega de permisos
El contrato de ataque llamó a la función del keeper 0x8804d1de y le pasó el adaptador recién creado en 0xf09. Luego, el keeper llamó a las funciones drip() y vat() del módulo, verificó mediante Vat.can() si ya se le habían otorgado permisos, y llamó a Vat.hope(). Esto le dio al módulo permisos ilimitados para administrar la cuenta del keeper en el sistema Vat.
El callback
Luego, el keeper llamó a la función join() del contrato de ataque. Esto cedió el control al código escrito por el atacante en un momento en que los nuevos permisos ya estaban vigentes.
Liquidación de las subastas
Dentro del callback, el módulo llamó a la función deal(id) del Flipper ETH-A descontinuado para las subastas 1457, 1458, 1459 y 1460. En marzo de 2020, el keeper originalmente había ganado cada uno de los cuatro lotes de 50 ETH con una oferta de cero, pero nunca había liquidado los acuerdos. Una vez liquidadas las subastas, se acreditaron 200 ETH como colateral ETH-A en la cuenta del keeper en Vat.
Retiro de los fondos
El módulo leyó el saldo de colateral del keeper y usó los permisos que había obtenido para mover todos los 200 ETH de la cuenta del keeper en Vat a su propia dirección con la función flux(). Luego retiró el colateral a través de ETH-A GemJoin como 200 WETH y envió el WETH al contrato de ataque.
¿Qué hay en el núcleo de la vulnerabilidad?
La causa raíz del ataque fue la falta de control de acceso en la función 0x8804d1de en la implementación del keeper en 0x68399ed8aa33C5b43F863EE6782de492006A5546.
Todas las demás funciones privilegiadas del contrato, incluidos los wrappers del keeper para las operaciones hope, flux y move, primero ejecutan una verificación ds-auth y rechazan las llamadas no autorizadas con el error ds-auth-unauthorized.
La función 0x8804d1de, en cambio, acepta cualquier dirección como adaptador, llama a Vat.hope() para esa dirección si el keeper aún no le ha otorgado permisos, y luego llama a join() en la misma dirección.
Debido a que el keeper luego llamó a join(), el módulo del atacante pudo ejecutar su propio código en un momento en que estos permisos ya estaban vigentes.
Los cuatro lotes sin usar de 2020 hicieron que esta vulnerabilidad fuera rentable para el atacante. Cualquiera puede llamar a la función deal para una subasta.
DeFiTuna aprendió a redondear: un error lógico costó $570 mil en USDC20 jul 2026Leer másMovimiento de los fondos
El atacante recibió todos los 200 ETH directamente dentro de la transacción utilizada para explotar la vulnerabilidad. A las 06:19:35 UTC, apenas seis minutos después, el atacante comenzó a mover los fondos hacia Tornado Cash, enviándolos como 20 depósitos de 10 ETH.

