Full Report
Nomad bridge is a cross-chain protocol between Cosmos compatible blockchains, such as Moonbeam, and Ethereum. Being able to transfer ERC-20 coins between chains is amazing for scalability but is really hard to do correctly. 4 out of the 5 biggest hacks on rekt.news have been on bridges. When moving stuff across chain, there appears to be two functions for this: prove() and process(). The process() function is used for uses a transaction from one chain to another after proving it. To validate if the message hash is valid, the following line of code is ran: require(acceptableRoot(messages[_messageHash]); The following code can be broken into a few steps: The variable _messageHash is the keccak hash of the data being added to the blockchain. The map (dictionary in Python) is used to find the matching hash for this message. If it's valid, it will the expected root hash. Otherwise, it will return 0x0, since that's the default for a bad map find in Solidity. acceptableRoot() is called to ensure that the root hash of this message is valid. If it is, the transaction continues. Otherwise, the contract reverts. So, this looks like good code, what's the problem? During an update of the smart contract, the hash 0x0 was made into a valid root! Since a Solidity map defaults to 0x0, any fake transaction will pass. Wait, how is this repeatable? It turns out that the function acceptableRoot() had been updated to handle an ENUM in legacy code. Instead of having PROVEN and PROCESSED (which return true and false respectively) the legacy code checks to see if the time for the data passed in is 0. Since the structure will return a hash time that is not 0, this ALWAYS passes. Thanks to Zellic_io for tracing this down. For the repeatable effect, I'm still not 100% sure about it. According to source code the 0x0 hash would be set to a value of 2 (PROCESSED) then would never be usable again. The state to update does messages[_messageHash], making it so the specific call cannot be used again. However, we can remake this call with slightly different values over and over again! What is super crazy about this, is that the QuantStamp audit called this out as a potential issue. I never would have called this out, since it seemed like it was part of the design. The attack lost 190 million but it appears that several of these people are whitehats who will give the money back. Regardless, I'm sure that 75% is gone forever. All of the other platforms that rely on this bridge, such as Moonbeam and EVMO, drastically sank in value from this attack.
Analysis Summary
# Incident Report: Exploitation of the Nomad Token Bridge
## Executive Summary
The Nomad token bridge was exploited for approximately $190 million due to a critical vulnerability introduced during a smart contract update. Attackers leveraged a flaw where an uninitialized message root (0x0) was inadvertently marked as trusted, allowing any user to bypass security checks and process fraudulent withdrawals. The incident was characterized by a "decentralized" free-for-all, where hundreds of participants copied the original attacker's transaction data to drain funds.
## Incident Details
- **Discovery Date:** August 1, 2022
- **Incident Date:** August 1, 2022
- **Affected Organization:** Nomad Bridge
- **Sector:** Decentralized Finance (DeFi) / Blockchain Infrastructure
- **Geography:** Global / Decentralized
## Timeline of Events
### Initial Access
- **Date/Time:** August 1, 2022
- **Vector:** Exploitation of a logic error in the `Replica` contract.
- **Details:** The attacker identified that a previous contract upgrade had initialized the "trusted root" mapping with the value `0x0`. Because Solidity mappings return `0x0` for non-existent keys, any message that had not been recorded (a "fake" message) was automatically validated against the trusted `0x0` root.
### Lateral Movement
- **Blockchain Execution:** Unlike traditional network movement, the "movement" here involved the replication of the attack by secondary actors. Once the first transaction was visible on the public mempool, other users began copying the transaction data, replacing the original attacker’s address with their own to drain various ERC-20 tokens (WBTC, WETH, USDC, etc.).
### Data Exfiltration/Impact
- **Assets Stolen:** Approximately $190.3 million in various cryptocurrency assets were drained from bridge liquidity pools.
### Detection & Response
- **Discovery:** Quickly identified by the community and security researchers (e.g., samczsun, Zellic) as the bridge TVL (Total Value Locked) began dropping precipitously in a chaotic, multi-party drain.
- **Response Actions:** Nomad paused the bridge functionality once the exploit was confirmed, though most funds had already been removed. Nomad subsequently issued an appeal for the return of funds, offering a "whitehat" bounty.
## Attack Methodology
- **Initial Access:** Smart contract logic exploitation (Insecure Default/Improper Initialization).
- **Persistence:** Not applicable; the attack focused on one-time fund extraction.
- **Privilege Escalation:** Bypassing the `prove()` requirement, effectively granting the attacker the ability to authorize any withdrawal.
- **Defense Evasion:** Using public "copy-cat" transactions to blend in with a crowd of hundreds of addresses.
- **Discovery:** Forensic analysis of the `acceptableRoot()` function and the `Replica.sol` contract state.
- **Impact:** Forced depletion of contract liquidity.
## Impact Assessment
- **Financial:** Total loss of ~$190M. Approximately $30M+ was eventually returned by whitehats and ethical researchers.
- **Data Breach:** None (transactional transparency is inherent to blockchain).
- **Operational:** Total bridge shutdown; loss of liquidity for ecosystem partners like Moonbeam and Evmos.
- **Reputational:** Significant loss of trust; highlighting the risks of "upgradable" smart contracts and the "bridge" bottleneck in DeFi.
## Indicators of Compromise
- **Behavioral indicators:** Large, frequent calls to the `process()` function without corresponding `prove()` transactions.
- **Contract State:** `confirmations[0x0]` set to a non-zero value in the `Replica` contract.
## Response Actions
- **Containment:** Paused the bridge contracts to prevent further unauthorized withdrawals.
- **Eradication:** Deployed updated smart contract logic to ensure the `0x0` root is never considered valid.
- **Recovery:** Coordination with law enforcement and cryptocurrency exchanges to track and freeze stolen funds; establishment of a recovery address for whitehats.
## Lessons Learned
- **The "0th" Root Fallacy:** Never allow `0x0` or uninitialized values to be used as a valid security parameter in smart contracts.
- **Audit Implementation:** The risk was reportedly flagged in a prior audit by Quantstamp but was not sufficiently mitigated or was reintroduced during a subsequent upgrade.
- **Chaos Factor:** Public blockchains allow "copy-cat" attacks; a single exploit can lead to a mass-looting event as onlookers replicate successful transaction payloads.
## Recommendations
- **Rigorous Upgrade Procedures:** Treat contract upgrades with the same level of scrutiny as initial deployments, focusing on state initialization.
- **Static Analysis:** Implement CI/CD checks that flag the use of default mapping values in critical `require` statements.
- **Monitoring/Alerting:** Real-time monitoring of bridge TVL and automated pausing mechanisms (circuit breakers) when anomalous withdrawal patterns are detected.