Nomad Bridge Hack Analysis

Ramil Amerzyanov
consideritdone.tech
4 min readAug 5, 2022

As you may be aware, the Nomad Bridge was hacked a few days ago. The team at Consider It Done Technologies has performed an investigation to identify and document the root causes of the hack and what made it possible to expose this vulnerability on the mainnet. Our teams have analyzed the source code, github issues, and reviewed the security audit report from Quantstamp. You can also reference a good storyboard as a twitter thread by @samczsun.

Hack Explanation

The Nomad bridge contains a couple of smart contracts but the only affected smart contract is Replica deployed at address 0xb92336759618f55bd0f8313bd843604592e27bd8. The source code of this contract is located on Github.

The Contract is initialized at transaction 0x53fd9277…4cad with all empty parameters. Please note the especially important parameter commitedRoot.

Initialization parameters

On line 115 the confirmAt map initialized for this root with a of value 1. confirmAt is a map where the key is a Merkle root and the value is a timestamp. Later this time value will be compared with the block timestamp.

Nomad bridge handles incoming requests in the process function. The most interesting code is on line 192:

require(acceptableRoot(messages[_messageHash]), “!proven”);

messages is a mapping of message leaves to the MessageStatus. If message hash doesn’t exist in this map then it returns 0x0

messages map returns zero bytes

acceptableRoot is a function that verifies that a specific root is confirmed and current time is sufficient to process it.

Let’s demonstrate how it works with a malicious transaction. We can decode payload and get bytes input

Using Tenderly to debug this transaction we can get keccak hash:

Getting message hash using Tenderly

So, _messageHash is 0xd50199e4c43f3163192cc73c68dab3746347477036581db3a1c721f5a99f595a. Then we get the message status by this hash. It shows this hash is not proved and output of it is zero bytes.

Then it passes zero bytes to acceptableRoot function and it returns true because contract is initialized with zero commitedRoot.

That’s it! There are no additional verifications for transactions.

What went wrong?

A Web3 security company Quantstamp performed a security audit of the Nomad smart contracts. They audit code based on commit 17f0557 that was committed on Mar 25. But the on-chain deployed version is 1.0.0-rc.3 from June 20. As we can see a lot of changes we introduced and the later version that were not audited.

Also, the changes in this commit are where the critical bug was introduced and were not covered by tests properly.

Also, Quantstamp reported a number of issues in the initial audit that are very critical from our perspective but there is no evidence they were remediated by the Nomad team.

  • QSP-33 Trusted Actors Risk And External Agents Have Too Much Power
  • QSP-35 Upgradeable Contracts Can Be Working With Older Versions
  • QSP-38 Updater Stop Signing New Roots
  • QSP-40 Initializer Not Disabled On Implementation Contract

Conclusions

  • Issues identified in the security audit were not remediated after being identified during the initial audit review. And additional audits were not performed on subsequent versions of the code
  • Test coverage is not sufficient. As we can see in this issue, unit tests are implemented only for a small portion of smart contracts
  • There are missed test cases in the compromised PR when committed root is a zero bytes
  • Lack of deployment and operational rigor. Initializing with zero values looks like something was not completed properly and should not have been promoted in the mainnet

At CIDT team we believe in the importance of high-quality code and secure operations as a solid foundation for any organization. Performing development and secure operations projects in the Blockchain space, since 2016, CIDT has the a solid real world experience in securing platforms and developing code using secure first principals, allowing us to contribute to passing security audits through the analysis and remediation of qualified auditors findings.

--

--