Fondo
En mayo de 2026, el proyecto ShapeShift FOX Colony tuvo un ataque en el contrato EtherRouterCreate3 desplegado en Arbitrum. El atacante aprovechó la capacidad de 'autocall' en el mecanismo de transacciones de meta del contrato, junto con la lógica de autorización automática de DSAuth sobre address(this), eludiendo el modificador auth para reemplazar el componente central del enrutador del contrato, resolver, con una versión maliciosa. Luego, mediante delegatecall, vació todos los activos ERC20 que poseía el contrato. La esencia de este ataque es la 'conflicto semántico entre las transacciones de meta y el modo de autorización de autocall interno', lo que resultó en una completa elusión de permisos.

Resumen del ataque

Causa raíz de la vulnerabilidad
auto-llamada arbitraria de executeMetaTransaction: address(this).call(callData) no filtra selectores sensibles
El contrato EtherRouter es en sí mismo una arquitectura de proxy actualizable basada en resolver: para selectores de función desconocidos, fallback() llama a resolver.lookup(msg.sig) para encontrar la dirección de implementación y la ejecuta mediante delegatecall. La funcionalidad de meta-transacción (executeMetaTransaction) se enruta desde el antiguo resolver 0x7490022b0e44aa65c030ac0d6728382a29458fc5 a la implementación del contrato 0x4e7f1e1e263678590007e89b7e129686ba7758d4. Esta implementación del contrato no es open source, lo siguiente se basa en los resultados de la descompilación:


Esencia del problema: el diseño de executeMetaTransaction tenía la intención de permitir a los usuarios ejecutar ciertas operaciones no sensibles mediante firmas, pero no filtra el functionSignature. Los atacantes pueden usar su propia firma válida para hacer que el contrato llame a setResolver (dirección maliciosa).
Autorización automática de DSAuth.isAuthorized: src == address(this) se libera
EtherRouter.setResolver(address) protegido por el modificador auth, solo debería permitir llamadas del owner o autoridad:

Pero hay lógica de autorización automática de auto-llamada en DSAuth.isAuthorized(address src, bytes4 sig):

Cuando executeMetaTransaction se activa mediante address(this).call(setResolver(...)), el msg.sender que se ve en setResolver es el contrato mismo 0x5c59..., por lo que DSAuth lo libera automáticamente.
Visto de forma aislada, estos dos diseños no constituyen fallos evidentes: la autorización automática de auto-llamada es bastante común en muchos modelos de proxy, y la mecánica de meta-transacciones en sí es razonable. Pero cuando ambas lógicas coexisten, surge un conflicto semántico: la capacidad de "auto-llamada arbitraria" proporcionada por las meta-transacciones se enfrenta a la lógica de "auto-llamada es confianza" de DSAuth, lo que resulta en una cadena completa de bypass de permisos.
Enrutamiento dinámico de delegatecall de EtherRouter.fallback(): control completo transferido después de que el Resolver fue secuestrado

Después de que el resolver fue reemplazado, el atacante solo necesita llamar a cualquier selector de función que no exista en EtherRouter, y el fallback() delegará incondicionalmente a la implementación maliciosa controlada por el atacante.
Resolver malicioso y implementación de Drain: registro de mapeo de funciones sin autorización + vaciado de activos de address(this)
El atacante pre-desplegó dos contratos:
Resolver malicioso
0x4e321af09012e15a67756522187c05b108b7ee0a (no open source, descompilado):

Implementación maliciosa de Drain
0x0b971e0a8ecc7d5b2465c903cf75aeaedbfc39e2 (no open source, descompilado):

Fórmula de ganancias del ataque

Flujo del ataque
El proceso de ataque se completó en una sola transacción, toda la lógica se ejecutó en el constructor del contrato atacante temporal 0x835a701fd76b96a76ee84de037d41f059ee29f5c.
Fase uno: despliegue de infraestructura maliciosa
El EOA del atacante 0xeed236afb6967f74099a0a6bf078bc6b865fbf28 inicia la transacción, creando el contrato atacante temporal 0x835a701fd76b96a76ee84de037d41f059ee29f5c.
El contrato atacante llama a un resolver malicioso 0x4e321af09012e15a67756522187c05b108b7ee0a con set(bytes4,address), mapeando el selector de drain(address,address) 0x837971e4 a la implementación maliciosa de drain 0x0b971e0a8ecc7d5b2465c903cf75aeaedbfc39e2.
Fase dos: secuestrar el Resolver mediante auto-llamada de meta-transacción
El contrato atacante llama a executeMetaTransaction() del contrato víctima 0x5c59d0ec51729e40c413903be6a4612f4e2452da. Dado que esta función no está en el propio ABI de EtherRouter, la llamada entra en fallback(), que es enrutada por el antiguo resolver a la implementación de meta-transacción 0x4e7f1e....
executeMetaTransaction verifica la firma mediante ecrecover, recuperando la EOA del atacante, y la verificación de la firma pasa (el atacante usa su propia firma válida, no una firma falsificada).
executeMetaTransaction construye el calldata de auto-llamada setResolver(0x4e321af...) y ejecuta address(this).call(callData). En este momento, el contexto es el contrato víctima, por lo que msg.sender == 0x5c59.... DSAuth.isAuthorized() devuelve true porque src == address(this), y el resolver se reemplaza con éxito.
Fase tres: vaciar activos a través del Resolver secuestrado
El contrato atacante llama a la función drain(USDC, 0xeed236...) del contrato víctima. Esta función no está en el ABI nativo de EtherRouter, y entra en fallback(). El resolver secuestrado devuelve la implementación maliciosa de drain 0x0b971e0..., y el contrato víctima realiza un delegatecall a ella. El código malicioso consulta USDC.balanceOf(0x5c59...) y obtiene 132704591501 (es decir, 132,704.591501 USDC), y luego llama a USDC.transfer() para transferir directamente al EOA del atacante.
El contrato atacante llama nuevamente a drain(0xf929..., 0x835a701f...), siguiendo la misma ruta para transferir el token intermedio robado de 841086343608217839604694 unidades al contrato atacante.
El contrato atacante intercambia el token intermedio robado en el par 0x5f6ce0ca... a través del Router 0x4752ba5dbc23f44d87826276bf6fd6b1c372ad24 por 1.949506469643782660 WETH, que se transfiere directamente al EOA del atacante.
Cierre de ganancias

Seguimiento de fondos
Análisis de la EOA del atacante 0xeed236afb6967f74099a0a6bf078bc6b865fbf28 a través de SlowMist MistTrack:
Principales huellas:
Relay.link - $4,368.08, agregador DEX para intercambio de activos.
LI.FI - $137,073.66, agregador cross-chain/DEX.
Los fondos robados por el atacante se movieron a Spark.fi Saving, y SlowMist MistTrack continuará monitoreando el flujo de fondos de las direcciones relacionadas.

Conclusión
La lección clave de este ataque es: cuando un contrato tiene simultáneamente las semánticas de "auto-llamada arbitraria de meta-transacción" y "autorización automática de auto-llamada", ambas constituyen una cadena completa de bypass de permisos; esto no es un fallo de código puntual, sino el resultado inevitable de un conflicto semántico entre componentes. Los desarrolladores de contratos deben definir claramente los límites de las funciones sensibles al diseñar mecanismos de meta-transacción o relay, al menos manteniendo una lista de selectores prohibidos en executeMetaTransaction y usando con precaución la autorización incondicional de auto-llamada con src == address(this). El equipo de seguridad de SlowMist sugiere que los proyectos realicen una auditoría de seguridad externa completa antes de implementar.
Nota: Este artículo es un análisis técnico de seguridad, solo para referencia de aprendizaje, no constituye ningún consejo de inversión. Todos los datos en cadena provienen de información pública.

