On August 19, 2026, the well-known cross-chain bridge Allbridge came under attack, resulting in the theft of around $190,000. However, the attack itself was not carried out in a single day: the attacker had been preparing it for almost a month. We examine in detail the mechanism of this attack and how a flaw in the verification of cross-chain messages allowed the attacker to pass off a nonexistent USDC transfer as a real one.
What you need to know in advance
To understand how the attack was carried out, you first need to understand how Circle's CCTP protocol works and the cross-blockchain messaging system it uses.
A regular CCTP transfer goes through several stages. First, the TokenMessenger on the source network burns USDC. Then Circle's MessageTransmitter verifies the authenticity of the message and passes it on. After that, the TokenMessenger on the destination network receives the message, checks it and creates the corresponding amount of USDC. Finally, the Router, after confirming the actual receipt of funds, transfers the USDC to the user.
Message transmission is handled by the Circle MessageTransmitterV2 contract. It issues an attestation for the full message — a Circle-signed proof that a specific cross-chain message was indeed verified and authenticated. However, this attestation only says that the content of the message was certified by Circle. On its own, it does not prove that the amount specified in the message was actually transferred.
Token creation on the destination network is handled by Circle TokenMessengerV2. After MessageTransmitterV2 receives the message, it determines which contract should be called, based on the recipient address specified in the message header. In a normal CCTP operation, this address must correspond to the TokenMessengerV2 contract on the destination network. Then the source TokenMessenger, the token, the amount and the recipient address are checked, and only after all checks pass successfully are new USDC created.
The CCTP module in Allbridge's infrastructure sits further down this chain. It accepts completed deposits and turns them into records that the Router can recognize, after which the Router transfers the tokens to the user. This is exactly where a critically important detail appears.
At this stage it becomes clear that Circle's sendMessage function is a universal interface. Any user can call it and pass a fragment of cross-chain message content, which Circle then authenticates. The mere fact of calling this interface does not automatically lead either to the burning of USDC or to the creation of new USDC. A regular CCTP transfer additionally requires the participation of the TokenMessenger on the source network. First it burns USDC, and then it creates a message with information about the destroyed tokens. In the attack under review, the attacker bypassed this stage: he simply formed a cross-chain message without burning any USDC.
The root cause of the vulnerability
In the Allbridge CCTPTokenMessenger contract, the receiveCctpMessage function, intended for receiving cross-chain messages, did not check whether the sender field in the CCTP message header matched the address of the remote TokenMessenger. The recipient field was also not checked against the Circle TokenMessengerV2 contract.
As a result, MessageTransmitterV2.receiveMessage passed the received cross-chain message to the attacking contract, allowing it to effectively determine as if the tokens had been successfully created. At the same time, the Allbridge contract itself did not additionally check whether the Router's USDC balance had actually increased after the supposed token creation. Instead, it trusted the amount contained in the message that the attacker had formed himself, and recorded it as a balance available to receive. This amount was stored in the receivedTokenAmount value associated with the messageHash inside the cross-chain message's hookData.
There was no additional protection in the hookData either. When the Router performs a payout, it independently recalculates the messageHash based on the call parameters, and then looks for the internal deposit record previously created for that hash. Even before sending the message to Polygon, the attacker could independently compute the required hash for the same parameters and record the messageHash in the cross-chain message's hookData in advance. Thus, the record that the Router later found was an internal record created by the attacker himself, and had nothing to do with the actual receipt of funds.
As a result, the attacker was able to directly call the sendMessage of the Circle MessageTransmitterV2 contract on the Polygon network and form an arbitrary cross-chain message. After Circle signed this message, the attacker was able to successfully pass the attestation check on the Base network. As a result, the fake message was perceived as a real cross-chain deposit, after which the Router transferred the tokens to the address specified by the attacker.
How the attack on Allbridge was carried out step by step
1. On July 26, 2026, the attacker for the first time directly called the sendMessage function of the Circle MessageTransmitterV2 contract on the Polygon network. This was an ordinary cross-chain message sending operation. The transaction created a MessageSent event, but no actual burning of USDC occurred. Circle subsequently issued an attestation for the full message, and this stage proceeded normally — exactly as the protocol provides.
Data in a format characteristic of CCTP was placed in the message. The source network was specified as Polygon, the destination network as Base, and the declared amount was 1,000,000 USDC. The feeExecuted parameter was set to zero, and destinationCaller pointed to the Allbridge CCTPTokenMessenger contract.
At the same time, the attacker set his own contract as the recipient in the message header, specified the address of the configured remote Messenger as sourceSender in the message body, and placed the pre-computed Router messageHash in hookData. All these parameters were prepared so as to later pass Allbridge's checks.
2. After creating the malicious cross-chain message, the attacker waited about 24 days. The reason for the wait lay in the specifics of how the Router on the Base network works. Since it performs the function of forwarding funds, it usually does not hold a significant volume of liquidity for a long time.
Only on August 19, 2026, when the Allbridge relayer had just created about 191,112 USDC as a result of a genuine CCTP deposit by another user, did the Router's balance reach about 191,156 USDC.
These funds belonged to real users and represented cross-chain transfers awaiting further sending to recipients. The attacker waited for this very short period when the Router held enough liquidity to carry out his plan, and began the direct attack just six seconds after the genuine deposit arrived.
3. The attacking contract first called the receiveCctpMessage function of the CCTPTokenMessenger contract, passing the message and signature data (attestation) obtained from the previous transaction on Polygon.
The destinationCaller and sourceSender checks passed successfully, since both values had been chosen and formed in advance by the attacker himself. Then the attacking contract called the receiveMessage function of the Circle MessageTransmitterV2 contract.
Within this function, a callback to the attacking contract was executed, since it was precisely the one specified in the recipient field of the message header. The attacking contract simply returned a successful result, without performing the expected token-creation operation through the Circle TokenMessenger.
Allbridge also did not re-check the Router's balance and did not make sure that the contract being called was actually the Circle TokenMessengerV2. Instead, it directly recorded the value amount - feeExecuted in receivedMessages[messageHash]. Thus, a record of supposedly received 1,000,000 USDC appeared inside the system, although no corresponding deposit existed.
4. Since the cross-chain message pre-created by the attacker specified an amount of 1,000,000 USDC, while the Router's balance at that moment was only about 191,156 USDC, the attacker was short about 808,844 USDC to carry out the subsequent transfer.
To temporarily increase the Router's balance to the required value, the attacker used an Aave flash loan. This allowed him to borrow 808,844 USDC for the duration of a single operation and bring the Router's balance up to the amount specified in the fake message. After this, the conditions for further withdrawal of funds were met.
5. Then the attacker called the receiveToken function of the Router to conduct the settlement operation. Based on the call parameters, the Router independently recomputed the hash and obtained a value that fully matched the messageHash that the attacker had computed in advance and placed in hookData.
After this, the Router checked only one thing: whether there was a receivedTokenAmount record in the CCTPTokenMessenger contract corresponding to this messageHash, and whether it exceeded zero. The presence of such a record was effectively perceived as proof that a genuine deposit had been received. Since the Router withholds a fee of 0.1%, after deducting it, it directly transferred the remaining 999,000 USDC to the attacking contract.
Thus, throughout the entire operation, the Router checked only the presence of a corresponding internal credit balance in the CCTPTokenMessenger contract. It did not re-verify whether this credit corresponded to a real inflow of assets on the blockchain for an equivalent amount.
6. After receiving 999,000 USDC, the attacker returned the principal of the flash loan to Aave — 808,844 USDC — and paid about 404.422012 USDC as a fee for using it.
Most of the losses fell on real cross-chain deposits that had just arrived at the Router and had not yet been transferred to their rightful recipients. Notably, the 1,000 USDC of Router fees mentioned above were later also stolen by other attackers who reproduced the same attack method.
The attestation problem
The main cause of the attack was insufficient verification of the cross-chain message on Allbridge's part. A Circle attestation only proves that the content of the message was not changed after its authentication. It does not prove that the message actually resulted from the burning of tokens on the source network or their creation on the destination network.
Allbridge directly trusted the amount, the source address of the funds and the messageHash that the attacker had himself formed in the CCTPTokenMessenger message. This data was recorded as a balance available for withdrawal and effectively came to be perceived by the system as the real deposit size.
For cross-chain bridge systems, the key principle is the following: a correctly authenticated and verified message is a necessary condition for carrying out a cross-network settlement, but the basis for the Router's final payout must be the actual receipt of assets.
