El 19 de agosto de 2026, el conocido puente cross-chain Allbridge sufrió un ataque que resultó en el robo de alrededor de $190,000. Sin embargo, el ataque en sí no se llevó a cabo en un solo día: el atacante lo preparó durante casi un mes. Analizamos en detalle el mecanismo de este ataque y cómo un fallo en la verificación de los mensajes cross-chain permitió al atacante hacer pasar una transferencia de USDC inexistente por una real.
Lo que hay que saber de antemano
Para entender cómo se llevó a cabo el ataque, primero hay que comprender cómo funciona el protocolo CCTP de Circle y el sistema de transmisión de mensajes entre blockchains que utiliza.
Una transferencia habitual mediante CCTP pasa por varias etapas. Primero, el TokenMessenger en la red de origen quema USDC. Luego, el MessageTransmitter de Circle verifica la autenticidad del mensaje y lo transmite. Después, el TokenMessenger en la red de destino recibe el mensaje, lo comprueba y crea la cantidad correspondiente de USDC. Por último, el Router, tras confirmar la recepción efectiva de los fondos, transfiere los USDC al usuario.
De la transmisión de los mensajes se encarga el contrato Circle MessageTransmitterV2. Emite una attestation para el mensaje completo: una prueba firmada por Circle de que un mensaje cross-chain concreto realmente fue verificado y autenticado. Sin embargo, esa attestation solo indica que el contenido del mensaje fue certificado por Circle. Por sí sola no demuestra que la cantidad indicada en el mensaje realmente se haya transferido.
De la creación de tokens en la red de destino se encarga Circle TokenMessengerV2. Después de que MessageTransmitterV2 recibe el mensaje, determina qué contrato debe ser llamado, basándose en la dirección del destinatario indicada en el encabezado del mensaje. En una operación normal de CCTP, esa dirección debe corresponder al contrato TokenMessengerV2 en la red de destino. Luego se comprueban el TokenMessenger de origen, el token, la cantidad y la dirección del destinatario, y solo tras superar con éxito todas las comprobaciones se crean nuevos USDC.
El módulo CCTP en la infraestructura de Allbridge se sitúa más adelante en esta cadena. Acepta depósitos completados y los convierte en registros que el Router puede reconocer, tras lo cual este transfiere los tokens al usuario. Es justo aquí donde aparece un detalle de importancia fundamental.
En esta etapa queda claro que la función sendMessage de Circle es una interfaz universal. Cualquier usuario puede llamarla y pasar un fragmento del contenido de un mensaje cross-chain, que Circle luego autentica. El mero hecho de llamar a esta interfaz no conduce automáticamente ni a la quema de USDC ni a la creación de nuevos USDC. Una transferencia habitual de CCTP requiere además la participación del TokenMessenger en la red de origen. Primero quema USDC y luego crea un mensaje con información sobre los tokens destruidos. En el ataque analizado, el atacante se saltó esta etapa: simplemente formó un mensaje cross-chain sin quemar ningún USDC.
La causa raíz de la vulnerabilidad
En el contrato Allbridge CCTPTokenMessenger, la función receiveCctpMessage, destinada a recibir mensajes cross-chain, no comprobaba si el campo sender del encabezado del mensaje CCTP coincidía con la dirección del TokenMessenger remoto. El campo recipient tampoco se comprobaba frente al contrato Circle TokenMessengerV2.
Como resultado, MessageTransmitterV2.receiveMessage transmitía el mensaje cross-chain recibido al contrato atacante, permitiéndole determinar de hecho como si los tokens se hubieran creado con éxito. Al mismo tiempo, el propio contrato de Allbridge no comprobaba adicionalmente si el balance de USDC del Router había aumentado realmente tras la supuesta creación de tokens. En su lugar, confiaba en la cantidad contenida en el mensaje que el propio atacante había formado, y la registraba como balance disponible para recibir. Esa cantidad se guardaba en el valor receivedTokenAmount asociado al messageHash dentro del hookData del mensaje cross-chain.
Tampoco había protección adicional en el hookData. Cuando el Router realiza un pago, recalcula por sí mismo el messageHash a partir de los parámetros de la llamada, y luego busca el registro interno de depósito creado previamente para ese hash. Incluso antes de enviar el mensaje a Polygon, el atacante podía calcular por su cuenta el hash necesario para esos mismos parámetros y registrar de antemano el messageHash en el hookData del mensaje cross-chain. Así, el registro que el Router encontraba después era un registro interno creado por el propio atacante y no tenía relación con la recepción efectiva de fondos.
Como resultado, el atacante pudo llamar directamente a sendMessage del contrato Circle MessageTransmitterV2 en la red Polygon y formar un mensaje cross-chain arbitrario. Después de que Circle firmara ese mensaje, el atacante pudo superar con éxito la comprobación de la attestation en la red Base. Como resultado, el mensaje falso fue percibido como un depósito cross-chain real, tras lo cual el Router transfirió los tokens a la dirección indicada por el atacante.
Cómo se llevó a cabo paso a paso el ataque a Allbridge
1. El 26 de julio de 2026, el atacante llamó por primera vez directamente a la función sendMessage del contrato Circle MessageTransmitterV2 en la red Polygon. Fue una operación habitual de envío de un mensaje cross-chain. La transacción creó un evento MessageSent, pero no se produjo ninguna quema real de USDC. Posteriormente, Circle emitió una attestation para el mensaje completo, y esta etapa transcurrió con normalidad, exactamente como prevé el protocolo.
En el mensaje se colocaron datos en un formato característico de CCTP. Como red de origen se indicó Polygon, como red de destino, Base, y la cantidad declarada era de 1,000,000 USDC. El parámetro feeExecuted se estableció en cero, y destinationCaller apuntaba al contrato Allbridge CCTPTokenMessenger.
Al mismo tiempo, el atacante estableció su propio contrato como recipient en el encabezado del mensaje, indicó en el cuerpo del mensaje la dirección del Messenger remoto configurado como sourceSender, y colocó el messageHash del Router calculado de antemano en el hookData. Todos estos parámetros se prepararon de modo que posteriormente superaran las comprobaciones de Allbridge.
2. Tras crear el mensaje cross-chain malicioso, el atacante esperó unos 24 días. La razón de la espera radicaba en las particularidades del funcionamiento del Router en la red Base. Como cumple la función de reenviar fondos, normalmente no mantiene un volumen significativo de liquidez durante mucho tiempo.
Solo el 19 de agosto de 2026, cuando el relayer de Allbridge acababa de crear unos 191,112 USDC como resultado de un depósito CCTP genuino de otro usuario, el balance del Router alcanzó unos 191,156 USDC.
Esos fondos pertenecían a usuarios reales y representaban transferencias cross-chain a la espera de ser enviadas a sus destinatarios. El atacante esperó a ese periodo tan corto en el que el Router tenía suficiente liquidez para llevar a cabo su plan, y comenzó el ataque directo apenas seis segundos después de que llegara el depósito genuino.
3. El contrato atacante llamó primero a la función receiveCctpMessage del contrato CCTPTokenMessenger, pasando el mensaje y los datos de firma (attestation) obtenidos de la transacción previa en Polygon.
Las comprobaciones de destinationCaller y sourceSender se superaron con éxito, ya que ambos valores habían sido elegidos y formados de antemano por el propio atacante. Luego el contrato atacante llamó a la función receiveMessage del contrato Circle MessageTransmitterV2.
En el marco de esta función se ejecutó una devolución de llamada al contrato atacante, ya que era precisamente él el indicado en el campo recipient del encabezado del mensaje. El contrato atacante simplemente devolvió un resultado exitoso, sin realizar la operación esperada de creación de tokens a través del Circle TokenMessenger.
Allbridge tampoco volvió a comprobar el balance del Router ni se aseguró de que el contrato llamado fuera realmente el Circle TokenMessengerV2. En su lugar, registró directamente el valor amount - feeExecuted en receivedMessages[messageHash]. Así, dentro del sistema apareció un registro de unos supuestamente recibidos 1,000,000 USDC, aunque no existía ningún depósito correspondiente.
4. Como el mensaje cross-chain creado de antemano por el atacante indicaba una cantidad de 1,000,000 USDC, mientras que en el balance del Router en ese momento había solo unos 191,156 USDC, al atacante le faltaban unos 808,844 USDC para realizar la transferencia posterior.
Para aumentar temporalmente el balance del Router hasta el valor necesario, el atacante usó un préstamo flash (flash loan) de Aave. Esto le permitió tomar prestados 808,844 USDC durante una sola operación y elevar el balance del Router hasta la cantidad indicada en el mensaje falso. Después de esto, se cumplieron las condiciones para el posterior retiro de fondos.
5. Luego el atacante llamó a la función receiveToken del Router para realizar la operación de liquidación. A partir de los parámetros de la llamada, el Router recalculó por sí mismo el hash y obtuvo un valor que coincidía por completo con el messageHash que el atacante había calculado de antemano y colocado en el hookData.
Después de esto, el Router comprobó solo una cosa: si en el contrato CCTPTokenMessenger existía un registro receivedTokenAmount correspondiente a ese messageHash y si superaba cero. La presencia de tal registro se percibió de hecho como prueba de que se había recibido un depósito genuino. Como el Router retiene una comisión del 0.1%, tras deducirla transfirió directamente los 999,000 USDC restantes al contrato atacante.
Así, a lo largo de toda la operación, el Router comprobó únicamente la presencia de un balance de crédito interno correspondiente en el contrato CCTPTokenMessenger. No volvió a verificar si ese crédito correspondía a una entrada real de activos en la blockchain por una cantidad equivalente.
6. Tras recibir 999,000 USDC, el atacante devolvió a Aave el principal del préstamo flash -808,844 USDC- y pagó unos 404.422012 USDC de comisión por su uso.
La mayor parte de las pérdidas recayó sobre depósitos cross-chain reales que acababan de llegar al Router y aún no habían sido transferidos a sus legítimos destinatarios. Cabe destacar que los 1,000 USDC de comisiones del Router mencionados antes fueron robados más tarde también por otros atacantes que reprodujeron el mismo método de ataque.
El problema de la attestation
La causa principal del ataque fue la insuficiente verificación del mensaje cross-chain por parte de Allbridge. Una attestation de Circle solo demuestra que el contenido del mensaje no se modificó tras su autenticación. No demuestra que el mensaje realmente fuera resultado de la quema de tokens en la red de origen o de su creación en la red de destino.
Allbridge confió directamente en la cantidad, la dirección de origen de los fondos y el messageHash que el propio atacante había formado en el mensaje CCTPTokenMessenger. Esos datos se registraron como balance disponible para retirar y, de hecho, pasaron a ser percibidos por el sistema como el tamaño real del depósito.
Para los sistemas de puentes cross-chain, el principio clave es el siguiente: un mensaje correctamente autenticado y verificado es una condición necesaria para realizar una liquidación intercadena, pero el fundamento para el pago final del Router debe ser la recepción efectiva de los activos.
