The first Mass Exploit of a protocol— Nomad Bridge
- Nomad Bridge
- The Attack — A high level overview
- The Attack — A Detailed Breakdown
- The bug
- The mass attack
- Could it have been avoided?
- Why this attack was unique
- First 7 Exploiters
On Aug 1st, 2022 Nomad bridge experienced an exploit causing a loss of around $190M worth of assets.
The root cause of the attack is a bad initialization of a critical parameter in the contract that allowed the attacker to bypass the security checks and drain tokens from the bridge.
This attack was unique in the sense that it was easily replayable, and was in fact exploited multiple times by multiple users immediately after the original attacker.
Nomad Bridge, a cross-chain bridge between Ethereum, Moonbeam, Avalanche, Evmos and Milkomeda.
A blockchain bridge is a protocol connecting two blockchains to enable interactions between them. If you own bitcoin but want to participate in DeFi activity on the Ethereum network, a blockchain bridge allows you to do that without selling your bitcoin (transferring your bitcoin to WBTC on ETH network)
The Attack — A high level overview
In order to verify that a request for asset transfer from a bridge is valid, the user must pass an on-chain approval process. The approval process validates that the message has a valid merkle proof and was approved by a privileged user (or governance).
Unfortunately, the contract was initialized in such a way that any message with an unknown hash was approved. Therefore, the attacker was able to craft a malicious message that sent millions of $ in assets the ETH bridge held to his own wallets.
Shortly after the attack, other users realized that they could slightly modify the original attacker’s message and more funds out of the bridge.
The Attack — A Detailed Breakdown
- The Replica.sol contract, which contains the public
processfunction that handles external messages, was initialized with a committedRoot of 0x000…000
2. Here’s the code of the
As you can see
confirmAt[_committedRoot] = 1; According to step (1) _committedRoot was set to 0x000…000 and therefore confirmAt[0x000….000] = 1.
3. The public
process function verifies that the request it receives is valid, by calling
acceptableRoot with a value derived from the hash of the given message
4. And here lies the core mistake — The
acceptableRoot receives the result of
messages[hash(msg)], but in solidity, the default value for an unknown key is 0x000…000. Therefore, any unknown message hash will cause the
accetableRoot function to be called with a
_root parameter of 0x000…000, and recall from step 2 that
confirmAt[0x000.000] = 1.
5. To recap, the attacker calls the
process function with a blob of bytes containing the parameters of his request to the bridge.
process function validates the request is valid by hashing it and looking for its corresponding root in the
messages mapping and checking it in the
If the attacker uses a message with a hash that wasn’t set before in the
messages array, solidity will give it a default value of 0x000….000 which hash the value 1 in the
confirmAt, because of the constructor initialization in step 1.
6. After passing the require, the
handle method was called with the attacker’s parameters, which caused the bridge to send him an arbitrary amount of tokens to an address of his choice
According to our analysis, it looks like the bug occurred in the
process function validation logic.
The Replica contract contains another external function called
proveAndProcess . This function receives a proof from the sender and calls the
prove function to validate it against the Merkle Tree.
If it passes the
prove step, the
prove function sets the
messages[hash(msg)] value to the appropriate merkle root. This was the missing step that caused every unknown message to receive the value 0x000…000 if it was sent directly to the
It looks like the team made the
process function public by accident, and it didn’t have the correct logic for checking the messages it was given.
In addition, the team initialized the
confirmAt array with the default value (0x000….000) to a truth-y value, which meant all unknown messages are approved by default.
The combination of these two factors allowed the attacker and other users to easily exploit the bridge.
The mass attack
As mentioned above, this was the first time so many exploiters raced to exploit the same transaction at the same time. This section will show how bitliq.eth, the original exploiter, tested his exploit in the wild for 2 days, how other exploiters caught up to him and how the exploiters even tried to frontrun each other, spending up to 4,000$ on gas fees and making the other exploiters’ transaction fail.
- The exploit was first used by bitliq.eth on July 31st, 2022-
2. bitliq.eth used the exploit again on July 31st, 2022 — https://etherscan.io/tx/0xa691d0cd3f8f63d65eabca171c85e12732d0540d4d84e9888896593aa7f0c43d
both of the attempts were done with a small amount of funds, probably as part of a testing phase. One attempt actually drained an ERC20 he himself deployed, probably to further test the exploit.
one can only assume that bitliq.eth wasn’t an experienced attacker because he tested a zero-day exploit in production, and it took him nearly 2 days to start draining a significant amount of funds.
Note: Exploiters usually prepare their attacks in a simulated environment to make sure the active exploit time is as fast as possible — optimally one single tx that drains as much funds as possible.
3. blitiq.eth proceeded with his attempts until someone else used his exploit and stole 100WBTC.
Second Exploiter Address — 0xb5c55f76f90cc528b2609109ca14d8d84593590e
Second Exploiter Tx — https://etherscan.io/tx/0xb1fe26cc8892f58eb468f5208baaf38bac422b5752cca0b9c8a871855d63ae28
At this point bitliq realized someone figured out his zero-day and probably understood that it’s time to steal a meaningful amount of money.
One interesting thing to note, is that the second exploiter tried to front-run bitliq.eth transactions
For example —
In this tx the second attacker spent 4200$ on gas fee https://etherscan.io/tx/0xb1fe26cc8892f58eb468f5208baaf38bac422b5752cca0b9c8a871855d63ae28
Whereas bitliq.eth (the first attacker) spent only 5.16$ — https://etherscan.io/tx/0xa5fe9d044e4f3e5aa5bc4c0709333cd2190cba0f4e7f16bcf73f49f83e4a5460
4. Then the third exploiter joined the attack and stole 100 BTC
Third exploiter — 0xe4a4df7e1689589efb06ec8c7c601b1cb193c5b3
Third exploiter TX — https://etherscan.io/tx/0x498a0778cc8448a10054d71508dee1a65b5f336daebba8ef859e712000391db9
5. Then the 4th, and 5th exploiters joined the attack.
4th exploiter — https://etherscan.io/address/0xa5eff6157b44d7eba6b003b72044bcb58faba036
5th exploiter — https://etherscan.io/address/0x56d8b635a7c88fd1104d23d632af40c1c3aac4e3
Once there were 6 different attackers, each one of them spent more than 2000$ on gas fees in order to make sure they would be able to withdraw before the others.
Its also interesting to note that even when there were 5 attackers actively exploiting the Nomad bridge, bitliq.eth still used 5.16$ in gas fees where the rest spent much more gas fees
The different attackers even sometimes caused each other to fail with a “SafeERC20 insufficient balance” error by running the exploit in consecutive blocks and racing each other
Judging by the time difference between txs, we can assume that everything was done manually and they were all nervous to maximize their gains. None of them seemed to have prepared in advance.
Could it have been avoided?
This attack could have been avoided by detecting that the
process functions had the same permissions, which means the
prove process was basically useless.
In addition, the bad initialization parameters are not part of a normal static code analysis of an audit, but could have been detected in a pen-testing process that involves the code and its realtime state.
Any of these steps should have been enough to prevent this attack from taking place
Why this attack was unique
This is the first attack at this scale of funds that was exploited by so many different addresses over such a long period of time.
In total, more than 8 different addresses stole more than 170M$ in the span of several hours, and the exploit was still being attempted by dozens of addresses hours after the pool was drained, but to no avail.
Exploiters usually prepare their attacks in a simulated environment to make sure the active exploit time is as fast as possible — optimally one single tx that drains as much funds as possible. However this wasn’t the case here, as according to our simulations, the original attacker could have improved upon his attack by modifying the payload to drain much more of the funds and from multiple pools much quicker.
It is unclear why the original attacker chose to leave so much funds up for grabs, but by doing so he created the first mass exploit of a protocol of this size.
First 7 Exploiters
- 0xa8c83b1b30291a3a1a118058b5445cc83041cd9d (bitliq.eth)
About Solid Group
Solid Group is a blockchain consulting and auditing service provider founded by cybersecurity experts with a great passion for the cryptocurrency world. We are known for our exceptional out of the box thinking, experience, and our credibility among the community. Throughout our work, our team was able to discover many high severity issues & vulnerabilities. We work with leading companies in the field, helping them increase their resilience through tailored services and solutions.
Solid Group provides ALL IN ONE ICO SOLUTION -
- audited token generator ( Generate your own token with NO CODING KNOWLEDGE)
- sniper bot protection tool
- Smart contract auditing service
🌍 solidgrp.io | 🐦 Twitter | 📣 Telegram Group | ✉️ firstname.lastname@example.org