
Nomad Hacked for $190 Million: A Post-Mortem of the Upgrade That Sparked a Disaster
TechFlow Selected TechFlow Selected

Nomad Hacked for $190 Million: A Post-Mortem of the Upgrade That Sparked a Disaster
During an upgrade, the contract marked 0x00 as a valid root, causing Nomad to be flooded with fraudulent messages. Attackers exploited this by copying and pasting valid transaction addresses, and amid the chaos, the bridge's assets were quickly drained.
Author: Samczsun
Translator: TechFlow intern
TL;DR
During an upgrade, the contract marked 0x00 as a valid root, causing Nomad to be flooded with fraudulent messages. Attackers exploited this by copying and pasting valid transaction data, simply replacing the recipient address, draining the bridge's assets amid the chaos.
The recent incident at Nomad is one of the most chaotic hacks I’ve seen in Web3, resulting in over $150 million drained. How did this happen? What was the root cause? Let me walk you through what went wrong behind the scenes.

It all started when CIA Officer shared Spreek’s tweet in the ETHSecurity Telegram channel. Although I didn’t know what was happening at first, seeing a large volume of assets leaving the bridge was clearly a bad sign.

My first thought was that there was a decimal misconfiguration in the token settings. After all, from my perspective, it looked like the bridge was running a "send 0.01 WBTC, receive 100 WBTC" promotion.

At first, I didn't believe it. However, after manually digging through some transactions on Moonbeam, I confirmed it was true—only 0.01 WBTC was sent from Moonbeam, yet somehow 100 WBTC were received on Ethereum.

Moreover, this WBTC bridging transaction didn’t actually go through a Prove step—it directly called `Process`. Processing a message without proof is clearly very dangerous.

There are two possibilities here: either the proof was submitted separately in an earlier block, or the Replica contract has a critical flaw. However, there was absolutely no indication that any message had been proven recently.

Therefore, only one possibility remained—the Replica contract had a fatal vulnerability. But how? A quick look at the code revealed that a submitted message must belong to an accepted root, otherwise the check on line 185 would fail.

Fortunately, there was a simple way to test this hypothesis. I knew that the root for an unproven message was 0x00, since messages[_messageHash] indicated uninitialized state. All I needed to do was check whether the contract would accept this root.

The contract accepted it…

As it turned out, during the upgrade, the Nomad team had initialized the trusted root to 0x00. Frankly, using a zero value as an initialization default is a common practice. Unfortunately, in this case, it had a tiny side effect: it automatically validated every single message.


This explains why the attack was so chaotic—you didn’t need to know Solidity or Merkle Trees. All you had to do was find a legitimate transaction, locate and replace the recipient address with your own, then rebroadcast it.
Join TechFlow official community to stay tuned
Telegram:https://t.me/TechFlowDaily
X (Twitter):https://x.com/TechFlowPost
X (Twitter) EN:https://x.com/BlockFlow_News














