Wormhole Guardian Signature Verification Bypass / Forged VAA Mint (2022-02-02)

  • Name: Wormhole guardian signature verification bypass / forged VAA mint
  • Date: 2022-02-02
  • Protocol: Wormhole / Portal Bridge
  • Chain: Solana + Ethereum
  • Category: bridge verification failure / signature-verification bypass / forged-message processing / unbacked wrapped-asset mint
  • Loss: about 120,000 weETH / wETH, worth roughly 326M at the time
  • Summary: An attacker exploited the Solana-side Wormhole core contract’s verify_signatures path because Wormhole did not keep guardian-signature evidence tied to the one runtime-authored instructions sysvar that was supposed to certify it. Wormhole expected instruction_acc to be the canonical Solana instructions sysvar because it uses prior secp256k1-verification instructions in the same transaction as proof that enough Guardians signed the VAA payload. But the vulnerable release never asserted instruction_acc.key == solana_program::sysvar::instructions::id(). That let attacker-controlled account data masquerade as runtime-authenticated instruction metadata, mark the signature_set quorum as valid, post a forged VAA, mint 120,000 unbacked Wormhole-wrapped ETH on Solana, and then redeem 93,750 of it back into native ETH from Ethereum-side bridge custody.
  • Trigger / entry point:
    • The Solana core contract’s verify_signatures function accepted an instruction_acc account from the caller.
    • The vulnerable code used solana_program::sysvar::instructions::load_instruction_at(...) over the supplied account data to parse the supposed prior secp256k1 verification instruction.
    • That API reads bytes from the provided account but does not itself prove the account is the canonical instructions sysvar.
    • Wormhole relied on the resulting signature_set account to authorize post_vaa, and Portal relied on that VAA to authorize complete_wrapped minting.
  • Root cause:
    • Wormhole treated a caller-supplied account as trusted Solana runtime metadata without validating that it was the real instructions sysvar.
    • On Solana, reference-only accounts are not implicitly trustworthy; programs must verify them explicitly before using them as security-critical context.
    • The missing identity check collapsed the guardian-authentication boundary because attacker-crafted bytes could masquerade as a successfully verified guardian-signature instruction stream.
    • More sharply: Wormhole let sysvar-shaped account data stand in for the canonical runtime-authored verifier context, so the verifier no longer had one protocol-owned root object for guardian-signature truth.
    • The bug sat in the bridge verifier itself, so downstream token minting logic faithfully executed a false authenticity claim.
  • Exploit path:
    1. The attacker created a fake account populated with data shaped like the Solana instructions sysvar and with a forged prior secp256k1-verification layout.
    2. They invoked Wormhole’s verify_signatures instruction and passed that fake account as instruction_acc.
    3. Because the program never checked that instruction_acc was the canonical instructions sysvar, load_instruction_at(...) parsed attacker-controlled bytes as if the Solana runtime had authored them.
    4. verify_signatures used that forged instruction metadata to mark enough entries in the signature_set account as true, satisfying Wormhole’s 2/3+ guardian-consensus rule without any real guardian signatures.
    5. With the forged signature_set, the attacker called post_vaa and created a malicious Wormhole message / VAA authorizing a mint to their own Solana address.
    6. The attacker then called complete_wrapped, which used Wormhole’s PDA mint authority to mint 120,000 weETH on Solana.
    7. They bridged 93,750 of that unbacked weETH back to Ethereum and redeemed it from Portal custody as native ETH, then swapped much of the remainder into SOL and USDC on Solana.
  • What the attacker needed to be true:
    • The Solana verifier had to trust user-supplied instruction-account data as if it were the genuine instructions sysvar.
    • The forged signature_set had to satisfy Wormhole’s guardian-consensus threshold for posting a VAA.
    • Portal’s mint path had to accept any Wormhole-authenticated message at face value and mint wrapped assets once the verifier returned success.
    • Ethereum-side custody still had to contain redeemable ETH backing legitimate wrapped ETH.
  • Technical anatomy worth remembering:
    • This was not a guardian key theft. The attacker forged the bridge’s proof that guardians had signed.
    • The critical state transition was fake instruction sysvar -> forged signature_set -> forged post_vaa -> legitimate complete_wrapped mint, not a direct token-contract bug.
    • The decisive lie was not merely signature parser bug; it was counterfeit verifier context became guardian truth. On this path, canonical sysvar identity was part of the signed-message security object, not auxiliary plumbing.
    • The missing check was tiny but decisive. Wormhole’s deployed fix added exactly this guard near the top of verify_signatures:
      • if *accs.instruction_acc.key != solana_program::sysvar::instructions::id() { return Err(SolitaireError::InvalidSysvar(*accs.instruction_acc.key)); }
    • The official incident report also matters for process lessons: a Jan. 13, 2022 public toolchain-upgrade commit had incidentally switched Wormhole toward the checked sysvar helpers, but that upgrade had not yet been released. The exploit therefore landed in the window between public disclosure of the safer pattern and onchain deployment of the fix.
    • Wormhole had to ship two containment layers after the core verifier fix: first patch verify_signatures, then separately blacklist already-prepared fraudulent VAAs the attacker had generated before the patch landed.
    • The exploit created unbacked wrapped ETH on Solana first, then converted verifier failure into Ethereum custody theft by redeeming those counterfeit wrapped assets through the normal bridge path.

Three bridge-truth promotions this incident collapsed

  1. A verifier-shaped guardian-signature context existed at all
    • The transaction carried an instruction_acc account and Wormhole could parse bytes from it with Solana’s instruction-introspection helpers.
    • At this stage the strongest true fact was only some sysvar-shaped account data exists and can be parsed, not the Solana runtime authenticated guardian-signature verification for this VAA.
  2. That parsed context was promoted into canonical truth that this exact guardian quorum had signed this exact VAA
    • This was the decisive failure. Because verify_signatures never proved instruction_acc was the canonical instructions sysvar, attacker-authored bytes were allowed to stand in for runtime-authenticated secp256k1 verification history.
    • The bridge therefore collapsed instruction-looking context is present and the runtime proved enough Guardians signed this message into the same verifier meaning, letting a forged signature_set become accepted VAA truth.
  3. That accepted bridge truth still deserved immediate mint and redemption authority
    • Once the forged signature_set was accepted, downstream post_vaa and complete_wrapped logic still treated the counterfeit VAA as ordinary mint authority for 120,000 weETH on Solana.
    • The route then preserved almost no blast-radius separation between one verifier result lied and live Ethereum custody can now be redeemed against that lie, which is why 93,750 weETH could be cashed out before operators fully contained the path.
  • Representative transactions and contracts:
    • Solana Wormhole core program: worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth
    • Malicious verify_signatures transaction cited by CertiK: 25Zu1L2Q9uk998d5GMnX43t9u9eVBKvbVtgHndkc2GmUFed8Pu73LGW6hiDsmGXHykKUTLkvUdh4yXPdL3Jo4wVS
    • Malicious post_vaa transaction cited by CertiK: 2SohoVoPDSdzgsGCgKQPByKQkLAXHrYmvtE7EEqwKi3qUBTGDDJ7DcfYS7YJC2f8xwKVVa6SFUpH5MZ5xcyn1BCK
    • Fraudulent 120,000 weETH mint transaction on Solana: 2zCz2GgSoSS68eNJENWrYB48dMM1zmH8SZkgYneVDv2G4gRsVfwu5rNXtK5BKFxn7fSqX9BvrBc1rdPAeBEcD6Es
    • Ethereum redemption transactions highlighted by Wormhole: 0x4d5201dd4a377f20e61fb8f42e6f929ec16bcec918f0584e39241d15b254a80f, 0xd31b155e259a403ebe69831fae0ec2b4bd33dfa090c43b605a57d5c72c4fbbc7, 0xacd309b02e4b533484d148de9ab0adf367ed4e70ed751d1ff036152dc3bc0479
    • Solana-side emergency verifier-fix governance transaction: 2G8nVB9MEhfmmF4BEE3AWAVWgSDnbevv5iJ69XymtA4wfgp7FugAx1UuajTBQzfwFNwyQjFffUJyPx1bymRweE1H
    • Solana-side follow-on blacklist / safeguard upgrade transaction: 5q8A9MjUXjRNxS3RTqRsFJs2ka4MAiYiACQvPdPk35Li7VCg1fA8gAz5ReohAbvDy6PUyf6brWLivDK2QX9VK6u2
    • Patch commit adding the missing sysvar-account check: e8b91810a9bb35c3c139f86b4d0795432d647305
    • Earlier public toolchain-upgrade commit that incidentally moved toward checked sysvar helpers: 7edbbd3677ee6ca681be8722a607bc576a3912c8
  • Response / mitigation:
    • Wormhole contributors coordinated with Guardians to stop relaying messages, effectively pausing bridge activity while the incident was contained.
    • The team developed and deployed a Solana-side contract fix that explicitly checked the instructions sysvar account before accepting signature-verification results.
    • Because the attacker had already prepared additional fraudulent messages before that fix landed, Wormhole later deployed extra Solana-side Portal safeguards to blacklist those specific malicious VAAs.
    • Jump Crypto recapitalized the Ethereum-side contract with 120,000 ETH so all Wormhole-wrapped ETH would remain fully backed when the network restarted.
    • Wormhole publicized a $10M whitehat / recovery offer and later reopened the network after patching and recapitalization.
  • Key transaction hashes:
    • Malicious verify_signatures transaction: 25Zu1L2Q9uk998d5GMnX43t9u9eVBKvbVtgHndkc2GmUFed8Pu73LGW6hiDsmGXHykKUTLkvUdh4yXPdL3Jo4wVS
    • Malicious post_vaa transaction: 2SohoVoPDSdzgsGCgKQPByKQkLAXHrYmvtE7EEqwKi3qUBTGDDJ7DcfYS7YJC2f8xwKVVa6SFUpH5MZ5xcyn1BCK
    • Fraudulent 120,000 weETH mint transaction on Solana: 2zCz2GgSoSS68eNJENWrYB48dMM1zmH8SZkgYneVDv2G4gRsVfwu5rNXtK5BKFxn7fSqX9BvrBc1rdPAeBEcD6Es
    • Ethereum redemption transactions: 0x4d5201dd4a377f20e61fb8f42e6f929ec16bcec918f0584e39241d15b254a80f, 0xd31b155e259a403ebe69831fae0ec2b4bd33dfa090c43b605a57d5c72c4fbbc7, 0xacd309b02e4b533484d148de9ab0adf367ed4e70ed751d1ff036152dc3bc0479
    • Emergency verifier-fix governance transaction: 2G8nVB9MEhfmmF4BEE3AWAVWgSDnbevv5iJ69XymtA4wfgp7FugAx1UuajTBQzfwFNwyQjFffUJyPx1bymRweE1H
    • Follow-on blacklist / safeguard upgrade transaction: 5q8A9MjUXjRNxS3RTqRsFJs2ka4MAiYiACQvPdPk35Li7VCg1fA8gAz5ReohAbvDy6PUyf6brWLivDK2QX9VK6u2

Pattern tags

  • bridge-verification-failure
  • Caller-supplied instruction_acc data was allowed to impersonate the canonical Solana instructions sysvar, so verifier context identity itself stopped being protocol-owned truth
  • verify_signatures produced a forged signature_set, and the rest of Wormhole faithfully treated that counterfeit verifier output as guardian consensus rather than re-proving it downstream
  • One false Solana-side guardian-authentication result still had immediate line-of-sight to complete_wrapped mint authority on Solana and then to live ETH redemption against Ethereum-side Portal custody
  • The Jan. 13 checked-helper commit versus Feb. 2 onchain fix gap preserved a painful operational lesson: once a safer verifier-boundary pattern is public, undeployed high-value routes are already living inside a patch-lag risk window

Why the exploit worked

  • Wormhole’s verifier accepted sysvar-shaped bytes instead of proving that the bytes came from the one runtime-authored instructions sysvar account. On Solana that identity check is not cosmetic plumbing; it is the thing that says prior secp256k1 verification instructions were actually emitted by the runtime and not authored by the caller.
  • The bridge’s real security object on Solana was not just guardian signatures exist, but the canonical instructions sysvar proves guardian signatures were verified for this VAA. Once instruction_acc could be substituted, the attacker did not need guardian keys; they only needed to counterfeit the verifier context Wormhole trusted to mint guardian truth.
  • Wormhole then faithfully amplified that forged result across the rest of the bridge. verify_signatures wrote a signature_set, post_vaa accepted it as quorum evidence, and complete_wrapped treated the posted VAA as sufficient mint authority. No later stage re-opened the authenticity question because the verifier had already answered it.
  • The economic loss came from bridge architecture, not just a local Solana parsing bug. The same false authenticity result that minted 120,000 unbacked weETH on Solana could still be redeemed back into native ETH from Ethereum-side Portal custody before operators contained the route.
  • Operationally, Wormhole’s own incident report makes the patch-lag lesson explicit: the safer checked-sysvar helper path had already become public through the toolchain-upgrade commit, but production had not yet rolled it out. For high-value bridge verifiers, known-better but not yet deployed is already part of the live risk surface.

Detection and audit pivots

  • Treat every caller-supplied account that claims to be runtime metadata, a sysvar, or another verifier-side root object as hostile until the program proves its canonical address and derivation. On Solana, looks like the instructions sysvar is not a security property.
  • Trace the full verifier-output chain, not just the local bug. A healthy review should ask what downstream state becomes authoritative if this verifier returns one false success? In Wormhole the answer was immediately signature_set -> post_vaa -> wrapped-asset mint -> Ethereum custody redemption.
  • Diff public commits, dependency upgrades, and helper migrations for newly introduced checked-API boundaries, then compare them against what is actually deployed. If the repository already contains a safer verification invariant than mainnet, the route is living with an observable patch-lag window on custody code.
  • Ask whether the protocol keeps one machine-checkable root object for signer / guardian truth or reconstructs that truth from caller-provided context plus parsing assumptions. If the latter, counterfeit context may be enough to forge consensus without compromising any signer key.
  • Model blast radius assuming the verifier lies once. If one forged VAA can still mint a route-scale wrapped asset and redeem it against deep destination custody before throttles fire, bounded-release controls are underdeveloped even if the verifier is usually correct.

Failure-prevention checklist

  • Prove canonical sysvar identity before parsing any runtime-authored context. If guardian-signature truth depends on the instructions sysvar, the verifier should fail before any parsing or hash comparison unless instruction_acc.key == solana_program::sysvar::instructions::id().
  • Keep one fused guardian-authentication object from signature check through mint authority. verify_signatures, signature_set, post_vaa, and downstream mint paths should preserve one machine-checkable story for these exact Guardians authorized this exact VAA, not reconstruct trust from caller-chosen context at each step.
  • Treat checked-helper migrations and dependency upgrades as live security work, not routine refactors. If a newer helper proves the canonical sysvar boundary that production does not yet enforce, undeployed routes are already carrying patch-lag risk on custody code.
  • Bound release even when the verifier is usually correct. One forged VAA should not have immediate line-of-sight to route-scale minting and deep destination-side redemption before operators can narrow the lane.
  • Add route-local emergency controls that can freeze already-prepared fraudulent messages, not just future verifier calls. Wormhole’s own response needed both the verifier fix and a later blacklist for forged VAAs already in flight.
  • Reconcile wrapped supply, posted VAAs, and destination-side custody continuously enough to detect counterfeit wrapped asset exists before most of it can be redeemed against canonical reserves.
  • Regression-test fake-runtime-context cases directly: forged instructions sysvar account, malformed prior secp256k1 instruction stream, forged signature_set progression, and end-to-end fake verifier output -> mint -> redeem simulations.
  • Budget route exposure so a single verifier-success event cannot drain the whole reserve surface. If one Solana-side authenticity lie can immediately reach Ethereum custody, the bridge has not separated verification risk from balance-sheet risk.

Audit prompts

  • Which exact account or object is the verifier trusting as runtime-authored context, and where is its canonical identity asserted before any parser reads bytes from it?
  • If verify_signatures returns one false success, what exact downstream objects become authoritative next: signature_set, post_vaa, mint authority, redeemability, or governance side effects? Which of those stages ever re-open the authenticity question?
  • Can caller-controlled accounts, PDAs, or instruction-order assumptions impersonate any part of the guardian-verification boundary even if signature parsing itself is locally correct?
  • Does the route have a public patch-lag window right now where repository code, helper migrations, or dependency updates already preserve a stronger verifier invariant than the deployed program?
  • What concrete controls stop one forged VAA from reaching route-scale wrapped-asset minting or destination-side reserve redemption before a pause lands?
  • Can responders invalidate already-prepared fraudulent messages after fixing the root verifier bug, or does the route assume patch deployed automatically means all forged artifacts are dead?
  • Which monitors compare wrapped supply, custody backing, and unusual verifier-success / post-VAA activity quickly enough to matter during a live exploit?
  • Does the route treat Solana runtime metadata, instruction introspection, and other reference-only accounts as custody-critical inputs during review, or are they still being audited like peripheral plumbing?

Lessons that should already be standard

  • On Solana, account identity is security logic. A parser that reads from the wrong account is not doing a weaker version of verification; it is often verifying nothing.
  • Runtime context is part of the signed-message security object. If a bridge depends on the instructions sysvar to prove that guardian signatures were checked, then which account supplied that context is as important as the signature math itself.
  • Patch-lag on custody code is a live attack surface. Once a safer verifier boundary exists in public source or dependency history, attackers may have both a bug hint and a deadline.
  • One verifier lie should not equal immediate reserve loss. Wormhole shows that bridges need bounded-release design in addition to correct verification, because counterfeit wrapped assets can become real destination-side losses very quickly.
  • Fixing the root bug may still leave forged artifacts alive. Teams need a second response playbook for already-prepared messages, queued releases, or other exploit residue that remains executable after the primary patch lands.
  • No guardian key was compromised is not a comforting postmortem fact if the verifier could mint guardian truth locally. Contract-side authenticity failures are custody failures.

Why this incident matters

  • It is one of the clearest examples of a bridge losing custody because its verifier accepted forged authenticity, not because its validator keys were stolen.
  • It shows how wrapped-asset solvency depends entirely on the correctness of the destination-side verifier. Once that verifier lies, wrapped asset becomes counterfeit IOU instantly.
  • It is also a durable Solana-specific lesson: untrusted account substitution is not a niche footgun when programs rely on sysvars, PDAs, and cross-instruction introspection for security-critical logic.
  • More broadly, it preserves a reusable design rule for the corpus: runtime metadata identity is part of the verifier root-of-truth object. If a bridge accepts sysvar-shaped bytes or other caller-authored verifier context instead of the one canonical runtime-authored object, guardian signatures can be forged without touching a guardian key.
  • It is also a strong bounded-release case study: one forged guardian-authentication result was enough to mint 120,000 counterfeit Solana-side wrapped ETH and redeem 93,750 of it against live Ethereum custody before operators could freeze the route, showing how little separation existed between false bridge truth accepted and nine-figure custody loss.

Internal linkages

  • Core recurring pattern: bridge-verification-failure.
  • Positive-design companion: bridge-truth-security-object — useful when the sharper question is what one canonical guardian-authenticated object verify_signatures and post_vaa should have bound before any mint or redemption path honored it.
  • Positive-design companion: root-object-authenticity-and-trust-anchor-recomputation-discipline — useful when the sharper question is what exact Solana runtime-authored root object Wormhole should have recomputed or pinned before any caller-supplied account data was allowed to define guardian-verification reality.
  • Positive-design companion: bridge-bounded-release-design — useful when the sharper question is why one forged VAA path was still allowed to mint 120,000 weETH and redeem most of it against live Ethereum custody before operators could react.
  • Adjacent root-object seam: account-confusion-root-of-trust-substitution — Wormhole is also a clean fake trusted sysvar case, where the verifier first accepted the wrong root account before bridge-specific message authenticity collapsed.
  • Adjacent verifier-path contrast: proof-to-message-binding-failure — Hyperbridge and BNB Chain kept a real trusted root but detached it from the claimed message, whereas Wormhole failed one step earlier by letting attacker-controlled Solana account data forge the guardian-verification result itself.
  • Useful contrast: signer-quorum-compromise — Wormhole produced the same practical outcome as a stolen guardian quorum, but the important lesson is that no guardian keys were actually compromised; the verifier forged consensus on their behalf.

Control surface