In August 2022, the Nomad token bridge was exploited↗ for $190M. What’s interesting is that anyone could have pinpointed the vulnerable code relatively easily. That’s because the vulnerable code was unaudited.
We’ve built a tool, called the Audit Coverage Tracker↗, which tracks what code is audited and unaudited for the biggest DeFi protocols. In this blog post, we’ll explore how the Nomad bridge bug could have been spotted months ahead of time.
Introduction
Of the top 20 hacks currently on the Rekt Leaderboard↗, 15 out of 20 were unaudited (including Nomad). While audits are not a perfect or complete solution to smart contract security, they are nonetheless a valuable line of defense. Unaudited code is much more likely than audited code to contain bugs or security vulnerabilities. That’s because in an audit, the scoped code is reviewed by a team of security professionals who are experts at finding bugs. And when hunting for bugs, attackers like to start by looking in places that no one else has–i.e., unaudited code.
In the case of Nomad, the bridge was audited by Quantstamp↗ in June 2022. According to Quantstamp, the buggy code responsible for the hack was introduced after the audit had begun↗. In other words, the smart contract code that was ultimately deployed differed from the audited code! And it was in this later-introduced code that a fatal bug was present.
This issue, or audit drift, is a well-known problem throughout the Web3 security industry. However, to the best of our knowledge, no one is tracking the on-chain audit drift of popular DeFi and Web3 protocols! There are billions of dollars locked in potentially unaudited code. This poses a serious, ongoing security risk to the community.
To solve this problem, we’ve developed an Audit Coverage Tracker↗, which we’ve made publicly available for the community to use. We’ve also analyzed several popular, large DeFi applications for audit drift and added them to the tracker. We invite the community to explore the results and share feedback.
On the tracker, you may also suggest additional projects↗ for tracking. For now, this feature is in beta, and submissions are manually reviewed for accuracy. Depending on community interest, we may automate this submission process in the future.
Now, let’s see how this audit tracker could have been used to spot the Nomad bug ahead of time!
Case study: Nomad Hack
In this case study, we’ll show how our audit tracker pinpoints the vulnerable code used to hack Nomad. But first, let’s review the facts of the Nomad hack.
The exploit was relatively simple. Most exploit transactions consisted of a single call to the process() function. Let’s look at that function:
function process(bytes memory _message) public returns (bool _success) {
bytes29 _m = _message.ref(0);
require(_m.destination() == localDomain, "!destination");
bytes32 _messageHash = _m.keccak();
require(acceptableRoot(messages[_messageHash]), "!proven");
require(entered == 1, "!reentrant");
entered = 0;
messages[_messageHash] = LEGACY_STATUS_PROCESSED;
IMessageRecipient(_m.recipientAddress()).handle(_m.origin(), _m.nonce(), _m.sender(), _m.body().clone());
emit Process(_messageHash, true, "");
entered = 1;
return true;
}
In the Nomad bridge, this function is responsible for executing cross-chain messages. This function’s security is absolutely critical. Every message in the bridge flows through this function. Not only must it accept valid messages, it must also reject invalid messages. It must also block “replay” attacks where a valid message is sent and reused multiple times.
In process, we can see that the function acceptableRoot is the main function responsible for checking whether a message is valid (“proven”) or not. The acceptableRoot function checks messages against an internal database of valid messages. That database is essentially stored using a Merkle tree, and the tree updated with new messages by an off-chain updater. This happens in the functions update and prove.
Using the audit coverage tracker↗, we can dig into Replica.sol, where all four of these functions live. Comparing the on-chain code versus the audited code, we can see that only 18.6% of the on-chain code in Replica.sol was audited! Keep in mind that this code is all critical to the secure functioning of the bridge. For comparison, LayerZero’s Stargate bridge is essentially 100% audited, apart from a very simple Router wrapper contract for native eth. This could have been a major red flag right away.
Digging further into the audit diff of Replica.sol, we can see major differences in the critical process function:
There are also significant changes to the acceptableRoot and prove functions:
Such large changes to critical functions in a cross-chain bridge could have been another red flag! Furthermore, the vulnerabilities used to hack Nomad were identified relatively quickly↗ after the hack. With the ability to quickly identify and locate unaudited code, chances are a security researcher might have found this bug before the hacker did. At the very least, these red flags could have been a warning to steer clear, that could have kept user funds out of harm’s way.
Conclusion
Hindsight is 20/20. It’s easy to blame unaudited code after a hack has already happened. But in the past two years, we’ve seen a number of hacks stem from unaudited code. In order for Web3 to achieve mass adoption, it’s essential that we learn from our mistakes. We need to systematically avoid these types of hacks from harming users going forwards.
In the future, we believe putting additional eyes on audited code will prevent devestating hacks before they happen. We hope that our audit coverage tracker will serve as a useful tool for the community moving forwards.
Of course, the tracker is by no means perfect in its current form. We are making a best-effort attempt to ensure accuracy, and we’re open to suggestions and feedback. Nevertheless, we believe the information it provides will be extremely useful to developers, users, and whitehats. By drawing additional attention to potentially unsafe code, we hope that the community can uncover, report, and fix security vulnerabilities before hackers do.
About Us
Zellic↗ is a smart contract auditing firm founded by hackers, for hackers. Our security researchers have uncovered vulnerabilities in the most valuable targets, from Fortune 500s to DeFi giants. Whether you’re developing or deploying smart contracts, Zellic’s experienced team can prevent you from being hacked.
Contact us↗ for an audit that’s better than the rest.