# Hack Analysis: Nomad Bridge, August 2022

Nomad Replica's April upgrade set 0x00 as trusted root, so any unproven message hash returned 0x00 and passed acceptableRoot, letting attackers spoof bridge messages and drain $190m.

- Payout: Real-world hack
- Severity: critical
- Program: Nomad Bridge
- Credited: Immunefi
- Disclosed: 2023-01-11
- Write-up quality: 9/10
- Original: https://immunefi.com/blog/bug-fix-reviews/hack-analysis-nomad-bridge-august-2022/
- Library page: https://bugbountylibrary.com/r/blog%3Aimmunefi-com-blog-bug-fix-reviews-hack-analysis-nomad-bridge-august-2022
- Tags: domain:web3 impact:funds-loss impact:mass-exploit kind:incident platform:immunefi sev:critical tech:evm tech:solidity technique:incomplete-fix technique:source-review vuln:access-control vuln:business-logic vuln:signature-replay

**Target:** Nomad Replica contract / ERC-20 bridge on Ethereum
**Root cause:** A routine proxy upgrade initialized committedRoot to 0x00, pre-approving it in confirmAt. Since unset mapping slots return 0x00, acceptableRoot(messages[hash]) returned true for any never-processed message hash.
**Steps:**
1. Fork Ethereum mainnet at block 15259100 (one block before first hack tx).
2. For each token, read ERC20_BRIDGE (0x88A69B4E698A4B090DF6CF5Bd7B2D47325Ad30A3) balance.
3. Build payload via `abi.encodePacked(MOONBEAM, uint256(uint160(BRIDGE_ROUTER)), uint32(0), ETHEREUM, uint256(uint160(ERC20_BRIDGE)), ETHEREUM, uint256(uint160(token)), uint8(0x3), uint256(uint160(recipient)), uint256(amount), uint256(0))`.
4. Call `IReplica(0x5D94309E5a0090b165FA4181519701637B6DAEBA).process(payload)`; require success.
5. Loop over WBTC, WETH, USDC, USDT, DAI, FRAX, CQT to drain all TVL in one transaction.
**How found:** Author read the Replica process()/acceptableRoot logic and the initialize() upgrade that set committedRoot=0x00, then reasoned that EVM mappings return 0x00 for unset keys, making any new message hash appear proven.
**Exploitation:** No flash loan or external protocol needed. Attacker crafts a fresh (never-processed) message with themselves as recipient and the bridge's full token balance as amount, then calls Replica.process. The unproven message hash maps to 0x00, which is an accepted root, so the message is dispatched to the ERC-20 bridge and funds transfer to the attacker. Replayable by anyone who edits the recipient field.
**Escalation:** First attacker drained 100 WBTC (~$2.3m); because the exploit was permissionless and replayable, a crowd of copycats changed the recipient address and replayed, collectively draining ~$190m.
**Insight:** When auditing proxy upgrades, check initialize() arguments for zero/default values that get written into trust mappings; combine with EVM's zero-default storage semantics so any unset mapping key inherits that trust. Also check whether message authentication relies on a mapping lookup that can be satisfied by an unused key.
