The first Mass Exploit of a protocol— Nomad Bridge

TL;DR

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

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

  1. The Replica.sol contract, which contains the public processfunction that handles external messages, was initialized with a committedRoot of 0x000…000

https://etherscan.io/tx/0x53fd92771d2084a9bf39a6477015ef53b7f116c79d98a21be723d06d79024cad

2. Here’s the code of the initialize function

As you can see Initialize sets 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.
The 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 confirmAt array.
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

The bug

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 process function

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.

https://etherscan.io/tx/0xa654fd4152f4734fcd774dd64b618b22a1561e2528b7b8e4500d20edb05b3ba0

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

https://etherscan.io/tx/0xbbb009a4b5cb19d8aeaff0b5a4285cb8de55e3010a530edb96dace5bf3acaff7

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 proveAndProcess and 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

  1. 0xa8c83b1b30291a3a1a118058b5445cc83041cd9d (bitliq.eth)
  2. 0xb5c55f76f90cc528b2609109ca14d8d84593590e
  3. 0xe4a4df7e1689589efb06ec8c7c601b1cb193c5b3
  4. 0xe4a4df7e1689589efb06ec8c7c601b1cb193c5b3
  5. 0x56d8b635a7c88fd1104d23d632af40c1c3aac4e3
  6. 0xbf293d5138a2a1ba407b43672643434c43827179
  7. 0x51841d9afe10fe55571bdb8f4af1060415003528

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 -

  • sniper bot protection tool
  • Smart contract auditing service

🌍 solidgrp.io | 🐦 Twitter | 📣 Telegram Group | ✉️ info@solidgrp.io

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Solidgroup

Solidgroup

822 Followers

We are a group 3 software developers with combined experience of over 15years in various fields such as Software design, Operating systems, and solidity.