| name | contracts-bridge-exploit |
| description | Cross-chain bridge attack — message-validation bypass (Wormhole class), validator key compromise (Ronin / Harmony class), reentrancy on token-bridge claim, Merkle-proof forgery on optimistic bridges, signature replay across chains. Bridges have lost >$2B; understand why. |
| allowed-tools | Bash Read Write |
| metadata | {"when_to_use":"bridge cross-chain wormhole ronin harmony nomad multichain anyswap layerzero axelar relayer validator merkle optimistic","subdomain":"contracts","tags":"defi, bridge, cross-chain, validator","mitre_attack":"T1190, T1565"} |
Cross-Chain Bridge Attack
Bridges hold large pooled assets and execute messages from a separate trust layer. Architecturally fragile.
Bridge taxonomy
| Type | Trust model | Past exploits |
|---|
| Lock-and-mint | Trusted validators sign mint events | Wormhole ($325M), Ronin ($600M), Harmony ($100M) |
| Burn-and-redeem | Same | Multichain/Anyswap ($126M) |
| Optimistic | Fraud proof window | Nomad ($190M) |
| Light-client / zk | Cryptographic | (Fewer hacks, but newer) |
| Liquidity-network (Hop, Connext) | Routers + relayers | Smaller individual exploits |
Attack classes
1. Validator-signature bypass (Wormhole, Feb 2022, $325M)
The Solana-side verify_signature function trusted the caller to provide a "verified" sigset account. Attacker passed a sysvar that wasn't a real sigset → unauthorized mint of 120k wETH.
Lesson: any verify function whose input is a struct fetched by address is bypassable if address-trust is missing.
2. Validator key compromise (Ronin, March 2022, $600M)
9-of-9 validators, 5 of which were operated by one entity. Sky Mavis got phished → attacker controlled 5 keys → arbitrary mint.
Lesson: validator decentralization isn't math, it's operational. Count distinct legal entities and key-storage methods, not just keys.
3. Merkle-proof forgery (Nomad, August 2022, $190M)
After an upgrade, the zero-hash was set as a "valid root" — any proof against root = 0 passed. The first attacker withdrew real funds; then thousands of others copy-pasted the same tx with their own address. ~$190M total.
Lesson: check initialize / upgrade scripts for accidental defaults — bytes32(0), address(0), 1 (often the most-tested value) becoming the trusted value.
4. Replay across chains (Multichain class)
Bridge tx signed for chain A is replayed on chain B because chainId wasn't bound into the signed message:
function claim(bytes32 nonce, uint256 amount, bytes calldata sig) external {
bytes32 hash = keccak256(abi.encode(nonce, msg.sender, amount)); // ⚠ no chainId
require(ecrecover(hash, ...) == validator);
token.transfer(msg.sender, amount);
}
// On chain A: legit claim
// On chain B (same validator, same token): replay the same sig
5. Reentrancy on bridge claim
Claim transfers tokens THEN updates nonce-used mapping:
function claim(...) external {
token.transfer(msg.sender, amount); // attacker token w/ callback
used[nonce] = true; // updated AFTER
}
// ERC777 / ERC1363 / malicious ERC20 callback → re-enter claim same nonce
6. Liquidity-pool draining via fee oracle (Hop / Across class)
Bridges using a quote-and-relay model price slippage via oracle. Manipulate the oracle → arbitrage drains liquidity:
Step 1: Borrow large position on chain A
Step 2: Manipulate underlying spot price oracle that bridge fee uses
Step 3: Initiate cross-chain transfer at the manipulated rate
Step 4: Repay loan, profit = bridge fee discount on a large transfer
Bridge audit checklist (the bugs that broke real bridges)
verify_signature trust chain: from where does the verifier load its trust roots? Can the caller spoof them?
- Validator set update: is there a quorum check on changing validators? Can a 51% rotate themselves out and stay in?
- Nonce vs chainId binding: does the signed message include destChainId?
- Claim state machine: nonce-used mapping written BEFORE external token call?
- Default values: any
address(0), bytes32(0), 1 in initialize that becomes a trusted value?
- Pause / rescue functions: are they gated by a multisig that's actually distributed?
- Relayer trust: does the bridge trust relayer-provided data, or re-verify on-chain?
- Fee/rate oracle: TWAP or spot? Manipulable?
Tooling
forge test --fork-url $RPC --match-contract Bridge --fork-block-number 17000000
slither <project>
echidna-test test/Bridge.t.sol --config echidna.yaml
OPSEC / scope
- Bridge attacks are direct financial theft. Never test against live bridges without explicit ownership/written authorization.
- Bug bounty bridges (Immunefi top payouts) are the legitimate channel — read the Immunefi scope, then test on a private fork.
- Disclose responsibly — bridge maintainers may need 24-72h to drain liquidity to a safe vault.
References
- Rekt.news — every major bridge hack post-mortem
- Trail of Bits "Cross-Chain Bridge Architecture" deep dive
- Wormhole / Ronin / Harmony / Nomad post-mortems (official)
- "How to Build a Secure Cross-Chain Bridge" — Chainlink Labs research