Securityadvancedarticle30 min read

Wormhole Bridge Hack: $326M Signature Verification Bypass | VCC

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 vulnerabil

Vietcardano teamTeam

Đội ngũ biên tập VietCardanoCommunity — các chuyên gia blockchain và Cardano, cung cấp nội dung giáo dục và nghiên cứu chuyên sâu cho cộng đồng.

Published September 14, 2026

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: verifySignatures on 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

Diagram - Wormhole Guardian Network Architecture: 19 guardians sign VAA -> target chains verify; Solana verifySignatures bugf verification passes, the message is truth.

The Vulnerability: Fake Sysvar, Skipped Signatures

On Solana, verifySignatures performs two checks:

  1. Timestamp validity: VAA timestamp < current time (from Clock sysvar)
  2. 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, vaa: &VAA) -> Result<()> { // BUG: No check that clock.key == SYSVAR_CLOCK_PUBKEY let clock = &ctx.accounts.clock; // Attacker-controlled account!

// 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

Diagram - VAA Verification Normal vs Exploit: Left normal flow 19 sigs verified, Right exploit sig loop skippedthe 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.


Diagram - Wormhole Hack Timeline: Feb 2-3 2022 exploit to bailout


The Numbers: $326M Stolen, Users Made Whole

MetricValue
wETH Minted120,000
USD Value (at time)~$326 million
ETH Price~$2,720
Transactions1 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.


Diagram - Fund Flow Jump Crypto Bailout: 120K wETH stolen -> users made whole

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

  1. Attacker creates a fake account with:
  2. key = random (not SYSVAR_CLOCK_PUBKEY)
  3. data = serialized Clock { unix_timestamp: 0, ... }
  4. No signature data
  5. Attacker builds VAA with:
  6. Payload: "deposit 120,000 ETH on Solana" (fake)
  7. signatures: empty array
  8. timestamp: 0
  9. Attacker calls verify_signatures with fake clock account + malicious VAA
  10. timestamp < clock.unix_timestamp → 0 < current_time ✓
  11. Signature loop: for sig in vaa.signatures → 0 iterations ✓
  12. 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.


Diagram - Defense Checklist 5 Lessons: Sysvar check, defense depth, formal verify, emergency rotate, protection fund


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:

`

  1. Receive VAA + clock account from caller
  2. CHECK: clock.key == SYSVAR_CLOCK_PUBKEY
  3. If false: REJECT (this is what Wormhole missed)
  4. Deserialize clock.unix_timestamp
  5. CHECK: vaa.timestamp < clock.unix_timestamp
  6. If false: REJECT (stale/future VAA)
  7. FOR each guardian in guardian_set:
  8. CHECK: guardian_index < vaa.signatures.len()
  9. If false: REJECT (missing signature)
  10. VERIFY: ECDSA.verify(vaa.signatures[i], guardian.pubkey, vaa.payload)
  11. If false: REJECT (invalid signature)
  12. 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.

Tags:

wormhole bridge hackwormhole 326 millionguardian signature bypassjump crypto bailoutsolana bridge hackwormhole exploit analysis