Ngày 1 tháng 8 năm 2022, nomad bridge hack diễn ra theo cách chưa từng thấy. Kẻ tấn công phát hiện Nomad Replica contract trên nhiều chain chưa bao giờ initialize trusted root — nó mặc định bytes32(0). Điều này có nghĩa là ai cũng có thể submit empty Merkle proof, và bridge sẽ chấp nhận vì zero hash bằng zero trusted root. Trong vài phút, 41 kẻ tấn công khác nhau copy exploit transaction, thay đổi recipient address, và rút $190 triệu across Ethereum, Moonbeam, Avalanche, và các chain khác. Đây là câu chuyện về nomad 190 million exploit, cuộc tấn công "crowdsourced hack" đầu tiên trong lịch sử DeFi, và những gì nó tiết lộ về nguy cơ uninitialized state trong cross-chain bridge.
TL;DR
- Chuyện gì xảy ra: Kẻ tấn công phát hiện Nomad Replica trusted root chưa initialize (zero hash), submit empty Merkle proof để rút $190M across 5 chain; 41 copycat replicate exploit trong vài phút
- Khi nào: 1 tháng 8, 2022 (exploit); 2-3 tháng 8 (whitehat recovery, Replica upgrade)
- Nguyên nhân gốc: Replica contract
initialize()chưa bao giờ gọi — trusted root mặc địnhbytes32(0); empty Merkle proof produce zero hash, match zero trusted root
- Attribution: Kẻ tấn công ban đầu ẩn danh (tinh vi, hiểu sâu Ethereum internals); 41 copycat attacker; không liên kết nation-state
- Tác động: Bridge hack multi-chain lớn nhất lúc đó; ~$190M rút across Ethereum, Moonbeam, Avalanche, Milkomeda, Evmos; ~$35M trả lại bởi whitehat
- Fix: Replica upgrade với
initialize()call đúng set trusted root thành actual Merkle root;process()reject zero-hash proof
Architecture: Multi-Chain Message Bridge
Nomad là optimistic cross-chain bridge kết nối Ethereum (Home chain) với nhiều destination chain (Moonbeam, Avalanche, Milkomeda, Evmos, v.v.). Khác với guardian model của Wormhole hay validator set của Ronin, Nomad dùng single Replica contract trên mỗi destination chain trust một "Home" contract trên Ethereum.
Trust model: Khi user lock token trên Ethereum Home, message được enqueue. Off-chain relayer watch event, construct Merkle proof, submit cho destination chain Replica. Replica verify Merkle proof với trustedRoot. Nếu valid, release bridged token.
Giả định critical: trustedRoot trong Replica được initialize đúng thành Merkle root của Home contract's message queue. Hàm process()
chỉ verify proof's root match trustedRoot.
Lỗ hổng: Zero-Hash Trusted Root
Trên mỗi destination chain, Replica contract có hàm initialize() set trustedRoot. Hàm này chưa bao giờ gọi trong deployment.
// Simplified Replica.sol (conceptual)
contract Replica {
bytes32 public trustedRoot;
function initialize(bytes32 _trustedRoot) external {
trustedRoot = _trustedRoot; // NEVER CALLED!
}
function process(bytes calldata message, bytes calldata proof) external {
bytes32 root = MerkleProof.verify(proof, message);
require(root == trustedRoot, "Invalid proof"); // BUG: trustedRoot = 0x0!
// Release funds...
}
}
Bug: trustedRoot mặc định bytes32(0) (zero hash). Empty Merkle proof cũng produce zero hash. Zero hash == zero trustedRoot → verification PASS.
Kẻ tấn công exploit bằng cách:
1. Gọi process() với empty proof (không Merkle branch)
2. Proof verification compute root = 0
3. root == trustedRoot → 0 == 0 → TRUE
4. Fund release mà không có message nào enqueue trên Ethereum Home
Cuộc tấn công: Copy-Paste Free-for-All
1 tháng 8, 2022, ~21:00 UTC — Kẻ tấn công phát hiện zero trustedRoot bug. Submit process() call đầu tiên với empty proof, rút fund từ Moonbeam Replica.
21:15 UTC — Exploit transaction visible trong public mempool. Calldata copy dễ dàng: chỉ change recipient address trong message.
21:30 UTC — Copycat swarm. Trong vài phút, 41 unique attacker submit version riêng của exploit, mỗi người change recipient thành address của mình. Fund rút đồng thời từ Moonbeam, Avalanche, Milkomeda, Evmos Replica.
22:00 UTC — Nomad team detect anomalous outflow. Pause Home contract (stop new message). Deploy Replica upgrade với initialize() call đúng.
2-3 tháng 8 — Whitehat hacker trả ~$35M cho Nomad multisig. $10M bounty paid. Còn ~$150M at large.

Con số: $190M Stolen, Crowdsourced Drain
| Metric | Value |
|---|---|
| Tổng steal | ~$190 triệu |
| Chain affected | Ethereum Home, Moonbeam, Avalanche, Milkomeda, Evmos |
| Unique attacker | 41 |
| Time window | ~60 phút |
| Whitehat return | ~$35M |
| Bounty paid | $10M |
| Still at large | ~$150M |
Đây là "copy-paste exploit" — exploit calldata identical cho mọi attacker ngoại trừ recipient address. Không cần tooling tinh vi; ai thấy tx đầu tiên trong mempool có thể copy, change recipient, replay.

Phân tích nguyên nhân gốc: Uninitialized Trusted Root
Proxy Pattern Pitfall
Nomad dùng proxy pattern: Home (Ethereum) → Replica (destination chain). Replica là upgradeable contract. Trong deployment:
1. Replica implementation deployed
2. Proxy deployed point to implementation
3. initialize() PHẢI gọi trên proxy để set trustedRoot
Step 3 bị miss. Replica proxy deployed nhưng initialize() chưa bao giờ gọi. Trong Solidity, uninitialized state variable mặc định zero. trustedRoot = bytes32(0).
Missing Check
// Should have been in process():
require(trustedRoot != bytes32(0), "Trusted root not initialized");
Single check này đã prevent toàn bộ exploit. Nhưng nó missing — code giả định initialize() luôn được gọi.
Exploit Construction
1. Kẻ tấn công build message: recipient = attacker_address, amount = max_token_balance
2. Tạo empty Merkle proof: proof = [] (không branch)
3. Compute root: MerkleProof.verify([], message) → return bytes32(0)
4. Gọi Replica.process(message, [])
5. Verification: root (0) == trustedRoot (0) → PASS
6. Fund release cho kẻ tấn công
Không signature, không cryptography broken — chỉ là default value bug.
Copy-Paste Free-for-All
Exploit copy dễ dàng. Malicious calldata visible trong public mempool. Ai cũng có thể:
1. Copy transaction calldata
2. Decode message, change recipient thành address của mình
3. Re-encode và submit
41 unique attacker tham gia — từ MEV bot tinh vi đến script kiddie với basic Ethereum knowledge. Đây là "crowdsourced hack" đầu tiên trong DeFi.
Tại sao lan nhanh:
- Public mempool visibility (không private mempool / Flashbots)
- Không commit-reveal hay encryption trên calldata
- Identical calldata cho mọi chain — chỉ change recipient
- Không rate limiting, không anomaly detection, không pause guardian
Whitehat Recovery & Nomad Response
2-3 tháng 8, 2022 — Ethical hacker (whitehat) bắt đầu trả fund cho Nomad multisig:
- ~$35M trả lại bởi whitehat drain để protect
- $10M bounty paid cho whitehat
- Replica upgrade deploy với
initialize()call đúng set trusted root
- Home contract paused để stop further processing
Tổng kết: ~$190M steal, ~$35M trả lại, ~$150M still at large. Nomad bridge paused vô thời hạn.
Bài học: Cái gì đã ngăn chặn được
1. Luôn initialize trusted state. Hàm initialize() PHẢI gọi khi deploy. Add constructor-time assertion hoặc factory pattern enforce initialization.
2. Reject zero làm trusted root. Explicit check require(trustedRoot != bytes32(0), "Trusted root not initialized"); trong process(). Zero không bao giờ là valid Merkle root.
3. Anomaly detection on-chain. Circuit breaker pause process() khi >X% TVL withdraw trong Y block. Nomad không có pause guardian.
4. Private mempool cho sensitive operation. Dùng Flashbots hoặc commit-reveal cho bridge processing transaction để prevent mempool copycat.
5. Proxy pattern safety. Enforce initialize() trên mọi implementation contract. Dùng factory pattern atomically deploy + initialize.
6. Rate limit trên bridge processing. Không bridge nào cho unlimited process() call mà không rate limit hay governance approval.

Câu hỏi thường gặp
Bao nhiêu bị steal trong Nomad bridge hack?
~$190 triệu across Ethereum, Moonbeam, Avalanche, Milkomeda, Evmos. ~$35M trả lại bởi whitehat; ~$150M still at large.
Đây là cryptographic break (Merkle proof broken)?
Không. Merkle proof verification logic đúng. Bug là trusted root chưa bao giờ initialize — nó mặc định zero. Empty proof produce zero hash, match zero trusted root.
Ai hack Nomad?
Kẻ tấn công ban đầu ẩn danh (tinh vi, hiểu sâu Ethereum internals). 41 copycat attacker replicate exploit. Không attribution nation-state.
User có mất tiền không?
Có — user có fund bridge qua Nomad trên chain affected mất fund. ~$35M trả lại bởi whitehat; ~$150M chưa recovered. Nomad bridge paused.
Tại sao 41 attacker copy exploit dễ vậy?
Exploit calldata visible trong public mempool. Exploit chỉ cần change recipient address trong message. Không cần cryptographic knowledge — chỉ change address và replay.
"Copy-paste exploit" là gì?
Exploit mà malicious transaction calldata reproduce dễ dàng bởi ai thấy nó. Modification duy nhất cần là change beneficiary address.
Nomad đã fix bridge chưa?
Rồi. Replica contract upgrade với initialize() call đúng set trusted root thành actual Merkle root. Hàm process() reject zero-hash proof. Bridge vẫn paused.
Bridge nào vulnerable với attack tương tự?
Bất kỳ bridge nào dùng uninitialized state variable, proxy pattern không enforce initialization, hoặc Merkle proof verification không zero-root check. Pattern "default value pass verification" là vulnerability class đã biết.
Technical Appendix: Correct Replica Initialization Flow
Cho developer, đây là initialization flow đúng mà Nomad đã thiếu:
1. Deploy Replica implementation contract
2. Deploy proxy point to implementation
3. CALL proxy.initialize(actual_merkle_root)
- Set trustedRoot = actual_merkle_root (non-zero)
4. VERIFY: trustedRoot != bytes32(0)
5. Only then: Replica ready to process message
Missing CALL initialize() ở step 3 đã tốn $190M.
Bài viết thuộc series DeFi Hacks. Để xem phân tích sâu hơn về bridge exploit, xem phân tích Wormhole hack, Ronin Bridge hack, và BNB Chain bridge hack.
