On February 2, 2022, the wormhole bridge hack stunned the crypto world. An attacker bypassed the signature verification of 19 institutional guardians and minted 120,000 wrapped ETH (wETH) on Ethereum — worth $326 million at the time — without depositing a single wei on Solana. The vulnerability was not a cryptographic break but a control-flow bypass: a single missing sysvar validation allowed the attacker to skip the entire guardian signature loop. This is the story of the wormhole 326 million exploit, the unprecedented Jump Crypto bailout, and what it revealed about the fragile trust assumptions of cross-chain bridges.
TL;DR
- What happened: Attacker submitted malicious VAA with fake sysvar account, skipped all 19 guardian signature checks, minted 120K wETH ($326M) on Ethereum
- When: February 2, 2022 (exploit); February 3 (bailout, patch)
- Root cause:
verifySignatureson Solana accepted fake sysvar account, timestamp check passed (0 < now), signature verification loop entirely skipped - Attribution: Unknown attacker (sophisticated, deep Solana internals knowledge); no nation-state link confirmed
- Impact: Largest DeFi hack at the time; Jump Crypto injected $325M to make users whole; guardian set upgraded
- Fix: Added sysvar key validation (
account.key == SYSVAR_CLOCK_PUBKEY), emergency guardian rotation mechanism
The Architecture: Guardians, VAAs, and Cross-Chain Trust
Wormhole is a generic message-passing bridge connecting Ethereum, Solana, and other chains. Its trust model rests on 19 guardians — institutional node operators (Jump Crypto, Certus One, Chorus One, Everstake, etc.) — who observe events on one chain, sign a payload, and produce a VAA (Verifiable Action Approval). The VAA contains the payload + 19 signatures. Target chains verify all signatures against the current guardian set. If valid, the message is executed.
The critical assumption: verifySignatures correctly validates all 19 signatures. The bridge has no other trust anchor — i
f verification passes, the message is truth.
The Vulnerability: Fake Sysvar, Skipped Signatures
On Solana, verifySignatures performs two checks:
- Timestamp validity: VAA timestamp < current time (from Clock sysvar)
- Signature validity: Loop through 19 guardians, verify each ECDSA signature
The bug: the function retrieved the Clock sysvar via account_info passed by the caller, without verifying that the account WAS the actual Clock sysvar. An attacker could pass a fake account that:
- Has
key != SYSVAR_CLOCK_PUBKEY(not the real sysvar) - Returns timestamp = 0 (always < current time)
- Has no signature data
When timestamp = 0, the check timestamp < current_time passes. But the fake account has no signature data, so the signature loop iterates zero times and returns Ok(()) — no signatures verified at all.
`rust // Simplified vulnerable logic (conceptual) pub fn verify_signatures(ctx: Context
// Timestamp check passes (attacker sets timestamp = 0) require!(vaa.timestamp < clock.unix_timestamp, TimestampError);
// Signature loop: iterates over guardian set // But if vaa.signatures is empty (attacker provides empty), loop runs 0 times for (i, guardian) in guardian_set.iter().enumerate() { // Never executes if vaa.signatures.len() == 0 verify_signature(vaa.signatures[i], guardian.pubkey, vaa.payload)?; } Ok(()) // Returns success with ZERO signatures verified! } `
The attacker crafted a VAA claiming "deposit 120,000 ETH on Solana" (which never happened), attached an empty signature array, and passed a fake Clock sysvar with timestamp = 0. The Solana bridge accepted it, emitted a "deposit completed" event, and
the Ethereum bridge — seeing a valid VAA — minted 120,000 wETH to the attacker.
Attack Timeline: 48 Hours That Shook DeFi
February 2, 2022, ~18:30 UTC — Attacker submits first malicious VAA to Wormhole core bridge on Solana. The VAA contains empty signatures + fake Clock sysvar.
18:35 UTC — Ethereum bridge processes VAA. verifySignatures passes (signature loop skipped). 120,000 wETH minted to attacker address 0x75D145....
19:00 UTC — Wormhole team detects anomalous minting. Guardian set paused. Bridge halted.
20:00 UTC — Security researchers (Neodyme, CertiK) identify root cause: verifySignatures sysvar substitution.
February 3, ~06:00 UTC — Jump Crypto announces $325M capital injection to backstop Wormhole users. Unprecedented VC bailout in DeFi.
12:00 UTC — Guardian set upgraded (emergency rotation). Patched verifySignatures deployed with require!(clock.key == SYSVAR_CLOCK_PUBKEY).
February 4-5 — Attacker returns ~$200M after negotiations; $10M bounty paid. Total ~$326M stolen, ~$200M returned, users made whole via Jump injection.
The Numbers: $326M Stolen, Users Made Whole
| Metric | Value |
|---|---|
| wETH Minted | 120,000 |
| USD Value (at time) | ~$326 million |
| ETH Price | ~$2,720 |
| Transactions | 1 exploit VAA + 1 mint tx |
| Time to Detect | ~30 minutes |
| Bailout Amount | $325M (Jump Crypto) |
| Attacker Returns | ~$200M |
| Whitehat Bounty | $10M |
| User Losses | $0 (fully backed by Jump) |
This was a "mint out of thin air" exploit — the attacker did not steal locked ETH; they convinced the bridge that a deposit occurred when it had not. The 120K wETH on Ethereum was unbacked. Jump Crypto's injection effectively recapitalized the bridge.
Root Cause Deep Dive: The verifySignatures Bypass
Solana Sysvar Model
Solana provides sysvars — special accounts with system data (Clock for timestamps, Rent for rent, StakeHistory for stakes). Programs access them via account_info passed in the instruction. The convention: programs should verify account.key == EXPECTED_SYSVAR_PUBKEY before trusting the data.
The Missing Check
Wormhole's verify_signatures instruction accepted a clock: Sysvar account parameter. The Anchor framework automatically deserializes the account data IF the account is the correct sysvar. But Wormhole used a generic AccountInfo and manual deserialization, bypassing Anchor's built-in check.
`rust // Vulnerable: accepts ANY account as "clock" #[derive(Accounts)] pub struct VerifySignatures<'info> { pub clock: AccountInfo<'info>, // No constraint! // ... }
// Should have been: #[derive(Accounts)] pub struct VerifySignatures<'info> { #[account(address = solana_program::sysvar::clock::ID)] pub clock: Sysvar<'info, Clock>, // Anchor enforces correct sysvar // ... } `
Exploit Construction
- Attacker creates a fake account with:
key= random (notSYSVAR_CLOCK_PUBKEY)data= serializedClock { unix_timestamp: 0, ... }- No signature data
- Attacker builds VAA with:
- Payload: "deposit 120,000 ETH on Solana" (fake)
signatures: empty arraytimestamp: 0- Attacker calls
verify_signatureswith fake clock account + malicious VAA timestamp < clock.unix_timestamp→0 < current_time✓- Signature loop:
for sig in vaa.signatures→ 0 iterations ✓ - Returns
Ok(())— zero signatures verified
Jump Crypto Bailout: Unprecedented in DeFi
Jump Crypto, a major Wormhole investor and guardian operator, announced a $325M USDC injection on February 3 to "make all Wormhole users whole." This was:
- Not a loan — capital injection to backstop the bridge
- Not insurance — no premium paid, no claim process
- VC bailout — sets precedent that well-funded bridges get rescued
Implications:
- Users made whole (120K wETH backed by Jump USDC)
- Moral hazard: VC-backed bridges may underinvest in security
- "Too big to fail" narrative enters DeFi
- Wormhole governance now includes "guardian emergency upgrade" mechanism
Lessons: What Would Have Prevented This
1. Always validate sysvar accounts. The fix is one line: require!(clock.key == SYSVAR_CLOCK_PUBKEY, InvalidSysvar);. This pattern must be mandatory for ALL sysvar usage in Solana programs.
2. Defense-in-depth: verify signatures even if timestamp passes. The signature loop should execute regardless of timestamp check. A timestamp check is a supplementary validation, not a substitute for signature verification.
3. Formal verification of verification code. The verify_signatures function is ~50 lines of critical code. It should be formally verified (Certora, Coq, Agda), not just audited. A single missed check cost $326M.
4. Guardian set emergency upgrade mechanism. Wormhole added a "guardian rotation" instruction post-exploit. This should exist before launch — the ability to quickly rotate compromised keys is essential.
5. Bridge user protection funds. Jump's bailout worked because they had capital and incentive. A protocol-owned insurance fund (like Maker's Surplus Buffer or a dedicated bridge protection pool) would institutionalize this.
6. Cross-chain asset backing transparency. wETH holders had no real-time visibility that their assets were unbacked. Bridges should publish real-time reserve proofs (Merkle trees, ZK proofs) so users can verify backing.
Frequently Asked Questions
How much was stolen in the Wormhole hack? 120,000 wETH were minted on Ethereum, worth ~$326M at ~$2,720/ETH. No user deposits were directly stolen — the attacker minted unbacked wETH.
Was this a cryptographic break (ECDSA broken)? No. The ECDSA signatures were never verified — the code path was skipped entirely via a fake sysvar account. This is a control-flow vulnerability, not a crypto break.
Who hacked Wormhole? Unknown. The attacker demonstrated deep Solana internals knowledge (sysvar model, instruction construction). No public nation-state attribution.
Did users lose money? No — Jump Crypto injected $325M to backstop all wETH. Users were made whole. However, this relied on a VC bailout, not protocol mechanisms.
Why could Jump Crypto bail out Wormhole but not other hacks? Jump is a major Wormhole investor and guardian operator with deep capital. Most DeFi protocols lack such a backstop. This highlights the centralization of VC-backed bridges.
What is a VAA and how was it forged? A VAA (Verifiable Action Approval) is a payload + 19 guardian signatures. The attacker did not forge signatures — they submitted a VAA with zero signatures and tricked the verifier into skipping the signature check entirely.
Has Wormhole fixed the bridge? Yes. The verifySignatures function now validates clock.key == SYSVAR_CLOCK_PUBKEY. Guardian set can be emergency-upgraded. Jump Crypto continues as backstop.
What bridges are vulnerable to similar attacks? Any bridge using Solana sysvars without key validation, or any verification code with control-flow bypasses. The pattern: "check A passes → skip check B" is a known vulnerability class.
Technical Appendix: Correct verifySignatures Flow
For developers, here's the correct verification flow that Wormhole missed:
`
- Receive VAA + clock account from caller
- CHECK: clock.key == SYSVAR_CLOCK_PUBKEY
- If false: REJECT (this is what Wormhole missed)
- Deserialize clock.unix_timestamp
- CHECK: vaa.timestamp < clock.unix_timestamp
- If false: REJECT (stale/future VAA)
- FOR each guardian in guardian_set:
- CHECK: guardian_index < vaa.signatures.len()
- If false: REJECT (missing signature)
- VERIFY: ECDSA.verify(vaa.signatures[i], guardian.pubkey, vaa.payload)
- If false: REJECT (invalid signature)
- If all checks pass: ACCEPT VAA, execute payload
`
The missing CHECK at step 2 cost $326M.
This article is part of the DeFi Hacks series. For more deep-dives on bridge exploits, see our analyses of the Ronin Bridge hack, BNB Chain bridge hack, and Nomad bridge hack.
