Wormhole (2022)

A $325M exploit against the Wormhole bridge between Solana and Ethereum. The attack was a signature-verification bypass: the bridge's Solana contract accepted a forged "guardian set" signature because of a missing check, letting the attacker mint 120,000 ETH-equivalent wormhole-wrapped-ETH (whETH) on Solana without locking ETH on Ethereum.

Timeline

  • February 2, 2022: Attacker minted 120,000 whETH on Solana with no backing.
  • Same day: Wormhole (Jump Crypto, the team backing Wormhole) acknowledged the incident.
  • Same day: Jump Crypto deposited 120,000 ETH from their own funds to make whETH holders whole, preventing a depeg.
  • February 3, 2022: Wormhole offered a $10M bounty for the attacker to return funds. The attacker did not.

Root Cause

The Wormhole bridge used a "guardian set" — a set of validator keys that sign attestations of cross-chain messages. The Solana program (solana/bridge/program/src/api/post_vaa.rs) had a function verify_signatures that verified guardian signatures via Solana's Secp256k1 syscall.

The verification flow involved a "signature account" — a Solana account that stored intermediate verification state. The bug: the program did not check that this signature account was created by the official secp256k1_verify_signatures instruction. An attacker could:

  1. Construct their own signature account with arbitrary data.
  2. Pass it to post_vaa, which checked the account's contents but not its provenance.
  3. The account claimed all signatures were valid; the bridge believed it.

Exploit Path

1. Attacker creates a "signature_set" account on Solana with crafted data.
2. The signature_set account is filled with bytes that claim "all 19
   guardian signatures verified successfully."
3. Attacker calls post_vaa on the Solana bridge program with a VAA
   (Verified Action Approval) that says "release 120,000 ETH worth of
   whETH to attacker's address."
4. post_vaa reads the signature_set account; sees the signatures-verified
   flag; mints 120,000 whETH.
5. Attacker swaps whETH for SOL, ETH, USDC on Solana DEXes and bridges
   some back to Ethereum.

The error in the underlying code was approximately:

#![allow(unused)]
fn main() {
// In post_vaa:
let signature_set = SignatureSet::deserialize(&account.data)?;
// ❌ no check that account.owner is the bridge's signature-verification program
require(signature_set.signatures.iter().all(|s| *s), "not all signed");
}

The fix: verify that the signature_set account is owned by the program that actually does Solana's Secp256k1 precompile-based verification.

What an Audit Should Have Caught

The auditor's question: when this code reads data from an account, where did that data come from? In Solana's account model, anyone can create an account with arbitrary data. A program that reads such an account without checking ownership is reading attacker-controlled data.

Specific findings:

  1. Missing account ownership check. The signature_set account's owner was not validated. Any account whose data deserialized successfully would be accepted.

  2. Trust-flag pattern in deserialized data. The signature_set struct contained a "verified" flag that the consumer trusted at face value. Trust flags should be impossible to forge — typically enforced via account ownership, not via field values.

  3. Solana-specific account model, where program-derived addresses (PDAs) and owner checks are central to security. The Wormhole code missed a basic Solana security pattern.

The Wormhole code had been audited multiple times by reputable firms. The bug was, in retrospect, surface-readable, but it required Solana-specific expertise to spot. This is an example of why audit firms need chain-specific specialists for cross-chain or non-Ethereum protocols.

Lessons

  1. Account ownership / origin verification is essential on Solana. Any data read from an account that the user passes in must be paired with a check that the account belongs to a trusted program.

  2. Cross-chain protocols need chain-specific auditors. A Solidity expert reviewing Solana code, or vice versa, will miss platform-native bugs. Bridges in particular need expertise on both sides.

  3. "It's been audited" is necessary but not sufficient. Wormhole had been audited by Neodyme and others. The bug survived. Auditors are human; multiple independent audits with diverse expertise reduce but do not eliminate risk.

  4. Bridges are honeypots. A bug worth $325M is worth significant adversarial investment. The economic incentive to find bugs in bridges is uniquely high.

  5. Backstops matter. Jump Crypto's decision to replenish the bridge's reserves prevented a contagion event (whETH depegging would have caused liquidations across many Solana DeFi protocols). Not every protocol team has the resources to do this. Audits should consider: "if a bug were exploited, what is the contagion path, and who absorbs the loss?"

  6. Forensics are valuable. The Wormhole exploit is exceptionally well-documented because Wormhole and the security community published detailed postmortems. New protocols should learn from these.

The Wormhole exploit, alongside Ronin and Nomad, made 2022 the year that the bridge category became broadly distrusted in DeFi. The industry response — better designs (CCIP, LayerZero v2, native bridges), more conservative integrations, more rigorous audits — is partial. Bridges remain the single largest loss category.