OAK — OnChain Attack Knowledge

Worked example · 2026-09

Liquid Network — unbacked L-BTC passed consensus, so the federation's 11-of-15 signers approved a peg-out that was valid in every way they were built to check — Liquid Network (Blockstream) / Bitcoin sidechain — 2026-09-06

Loss
~$320M — 3,996.01834922 BTC paid out on a single peg-out, plus 2.65138358 BTC and 3.99601658 BTC in two follow-on batches, for ~4,002.67 BTC total. The federation's Bitcoin reserve went from ~4,200 BTC that morning to 197 BTC. The recipient published an OP_RETURN reading "we are whitehats. contact us on chain" and negotiated with Blockstream through PGP-signed Bitcoin messages, conditioning any return on every bridge node being patched first. On 2026-09-07, after Blockstream published a signed message confirming the bridge nodes were patched, 3,400 BTC (~$269M) moved back to the federation's peg wallet — about 85% of the 3,998.50 BTC withdrawn. The recipient retained 598.50 BTC (~$47M, ~15% of the total), kept moving it, and embedded messages demanding a 10% bounty; Blockstream refused to pay and said it would pursue recovery. The realised loss therefore stands at ~598.5 BTC against ~4,002.67 BTC moved, and OAK records both: the first is what the operator is out, the second is what the defect was worth to anyone who found it. Third-largest crypto theft of 2026 to date by value moved.
OAK Techniques observed
OAK-T9.014 (Protocol-Client Consensus Bug — primary. The defect is in Elements, the reference client that defines Liquid's consensus, not in any smart contract or key. Blockstream's own statement is that the L-BTC came from an Elements bug and that "the SideSwap key was not compromised, nor were any others." See techniques/T9.014-protocol-client-consensus-bug.md). Three neighbouring Techniques explicitly do not apply, and saying so is the point of the case. OAK-T10.001 (Validator / Signer Key Compromise) — no key was stolen; enough functionaries signed voluntarily and correctly. OAK-T11.003 (Multisig Contract Manipulation) — the 11-of-15 threshold was not bypassed, degraded, or tricked; it was met. OAK-T5.003 (Hidden-Mint Dilution) — there was no mint authority to exercise; the supply was produced by consensus-layer validation failing to reject it.
Attribution
pseudonymous. On-chain identifiers only, plus PGP-signed Bitcoin messages to Blockstream. Funds consolidated to bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte. The party self-identifies as white-hat; Blockstream has not verified that claim and used the word "purported." OAK records the mechanism and treats the self-description, the partial return, the retained 598.50 BTC and the 10% bounty demand as disposition metadata, not as a reclassification of the event. That label was publicly disputed — Ledger's CTO characterised keeping ~15% as ransom-like — and OAK does not adjudicate it: what the party called itself changes nothing about what the consensus code did.
Key teaching point
A threshold signature can only attest to the questions its signers are built to ask, and "should this coin exist?" was not one of them. The functionary HSMs checked that the peg-out went to a whitelisted address and that the L-BTC burned matched the BTC requested. Both were true. The burned L-BTC was itself unbacked, and no part of the federation was responsible for knowing that — it was delegated to consensus rules that had silently stopped enforcing the invariant. So 11-of-15 hardware modules, an audited whitelist, and a working peg-out authorisation system all functioned exactly as designed while paying out 95% of the reserve. When a signing quorum sits downstream of a consensus check, the quorum inherits that check's correctness and adds nothing to it. The reusable control is not more signers or better key custody: it is a reserve-side invariant the federation enforces itself — total circulating L-BTC must equal BTC held, checked before signing, not derived from the same consensus that produced the request. That number was public and wrong for hours: explorers disagreed about block 4,050,336 (Blockstream's accepted it, mempool.space rejected it and stalled at 4,050,335), and afterwards the reserve read 197 BTC against ~4,205 L-BTC in circulation.

Summary

Liquid Network is a Bitcoin sidechain run by a federation of functionaries. BTC is locked on Bitcoin and L-BTC is issued 1:1 on Liquid; exiting requires a peg-out, in which the federation's 11-of-15 signers release real BTC from the reserve. Liquid uses Confidential Transactions, so amounts are hidden behind Pedersen commitments and validated by range proofs. Peg-outs are gated by Peg-out Authorization Keys (PAKs); SideSwap, a peg-out partner, holds a whitelisted PAK and builds peg-outs on customers' behalf from its own wallet.

On 2026-09-06 a transaction created L-BTC that was never backed by BTC. Because amounts are confidential, the extra coins were invisible to every observer — as one analysis put it, when the verification is wrong, nobody can see the difference. The unbacked L-BTC was routed through SideSwap's normal peg-out flow. SideSwap "had no way to tell those coins from any other L-BTC." At 14:28:56 UTC the federation paid 3,996.01834922 BTC to a fresh Bitcoin address.

Blockstream confirmed the bug was in Elements and that no keys were compromised, without publishing the defect. On 2026-09-11 SlowMist published a full analysis that names it: the range-proof verification cache built its key by concatenating variable-length fields without length prefixes, so two different argument sets could produce the same key. Two setup transactions in block 4,050,335 (13:52:10) placed valid rangeproofs and commitments on-chain, seeding node caches with a crafted payload; the third transaction, one block later, reused the colliding key with different field boundaries, and on the cache hit the nodes skipped both the cryptographic verification and the minimum-value check, accepting an unbacked commitment as proven. 3,998.5 L-BTC came from nothing. The cache optimisation dates to 2016.

The patch history needs stating precisely, because the intuitive reading of it is wrong. The 2026-08-03 upstream commit (212c43f4) added the missing fields to the cache key but did not protect their boundaries — it was an incomplete fix and would not have prevented this attack. Its cherry-pick to the 23.3.x branch (PR #1599) merged at 17:21 UTC on 2026-09-06, about three hours after the peg-out, which is why the first published accounts read it as a fix that shipped late. The defect was actually closed by Elements 23.3.4 on 2026-09-08, which added length prefixes to the cache key and shipped a -norangeproofcache emergency switch. The release running during the attack, 23.3.3 (2026-04-13), contained neither. No CVE has been assigned. OAK records the mechanism as operator-corroborated and independently attested (Blockstream: Elements bug, no keys compromised; SlowMist: the specific cache-key collision).

The signature of a consensus bug was visible in public infrastructure before anyone diagnosed it: block 4,050,336 was accepted by Blockstream's explorer and rejected by mempool.space, whose chain tip stopped at 4,050,335. Two nodes running what should be the same rules reached different verdicts on the same block.

Timeline (UTC, 2026-09-06 unless noted)

When Event OAK ref
2026-04-13 Elements 23.3.3 released — the shipped build lacking the later cache fix (standing T9.014 surface)
2026-08-03 Upstream commit 212c43f4 adds the missing fields to the range-proof cache key — but no length prefixes, so the collision stays reachable (partial fix, and unshipped)
13:52:10 Two setup transactions in block 4,050,335 place valid rangeproofs and commitments on-chain, seeding node caches with the crafted payload T9.014 — cache seeding
13:53:10 Third transaction in block 4,050,336 reuses the colliding cache key with different field boundaries; verification and minimum-value checks are skipped; 3,998.5 unbacked L-BTC created T9.014 exploitation
13:53:10 Blockstream's explorer accepts block 4,050,336, mempool.space rejects it and stalls at 4,050,335 T9.014 — consensus divergence, observable in public
~14:05 4,000 L-BTC sent into SideSwap's peg-out service; SideSwap builds the peg-out with its own whitelisted PAK (legitimate path, unbacked input)
14:06:10 Peg-out transaction in Liquid block 4,050,349 T9.014 exploitation
14:28:56 Bitcoin block 965,783: federation pays 3,996.01834922 BTC from bc1qdlld6a…uhwxxr; 11-of-15 HSMs sign — whitelist check and burn-amount check both pass (quorum met, invariant unchecked)
16:01:28 / 16:48:45 Two follow-on peg-out batches process (2.65138358 and 3.99601658 BTC) (continued drain)
18:23:18 An independent researcher flags the transaction publicly (external detection)
18:30:10 Bitcoin block 965,818: recipient's OP_RETURN — "we are whitehats. contact us on chain"; 1,000 sats sent to the federation wallet to guarantee visibility (disposition signal)
17:21 PR #1599 cherry-picks the cache fix into the 23.3.x branch — ~3 hours after the peg-out confirmed (patch lands post-hoc)
20:25 Blockstream statement: Elements bug, no keys compromised, bridge nodes disabled (operator response, T+5h56m)
2026-09-07 Blockstream publishes a signed message confirming bridge nodes are patched and safe for funds to be returned; 3,400 BTC (~85%) returned to the federation peg wallet; 598.50 BTC retained, with on-chain messages demanding a 10% bounty, which Blockstream refuses (partial recovery, open dispute)
2026-09-08 Elements 23.3.4 released — length prefixes added to the cache key, plus a -norangeproofcache emergency switch (the actual fix ships, T+2d)
2026-09-10 Liquid resumes block production in a controlled restart, without user transactions, while functionaries update nodes; peg-in and peg-out stay suspended (staged recovery)
2026-09-11 SlowMist publishes the cache-key collision analysis; Blockstream's CEO commits to covering the 1:1 peg, with no restoration date given (mechanism attested)

What defenders observed

  • The multisig was never the control it appeared to be. Every published defence of Liquid's security model rests on 11-of-15 hardware functionaries. That quorum answered its two questions correctly and released 95% of the reserve, because the question that mattered — is this L-BTC real? — was answered upstream by consensus code and simply passed through. A quorum downstream of a broken check ratifies the break.
  • Confidential Transactions removed the observer of last resort. In a transparent chain, an inflation bug shows up as arithmetic anybody can redo. Under Pedersen commitments the amounts are hidden by design, so a verification failure produces no visible artefact at all. Privacy at the value layer means the supply invariant must be enforced by explicit accounting, because it can no longer be spot-checked by inspection.
  • Client disagreement was the earliest available alarm and nobody was wired to it. Two independent explorers disagreed about block 4,050,336 roughly 13 minutes before the peg-out. Cross-implementation validity divergence is the canonical T9.014 detection surface, it was firing in public, and it was noticed only when a researcher looked, at 18:23 — four hours after the money left.
  • The patch that looked like the missed fix was not the fix. 212c43f4 landed 2026-08-03, the production release was from April, and the cherry-pick merged three hours after the money left — a perfect deployment-latency story, and the deployment gap is real. But the exploited defect was in the field boundaries, which that commit never addressed; only 23.3.4 (2026-09-08) closed it. Patch availability, patch deployment and patch sufficiency are three different states, and a review stopping at the first two would have concluded — wrongly — that shipping on schedule would have saved the reserve.
  • A reserve-parity check is cheap, external, and would have fired. Circulating L-BTC versus BTC held in the federation address is two numbers from two chains. Enforced as a pre-signing condition inside the functionaries, it makes a consensus inflation bug non-monetisable through the peg-out path regardless of what the consensus code believes.
  • The peg-out partner was structurally unable to help. SideSwap did nothing wrong and could not have: its whitelisted PAK is an authorisation instrument, not a validation one, and it had no means to distinguish backed from unbacked L-BTC. Delegated peg-out authority scales the blast radius of a consensus bug without adding a check anywhere.

Public references

Discussion

T9.014 has been the corpus's thinnest well-defined Technique: one canonical anchor from 2010 (the Bitcoin value overflow bug), one unexploited disclosure (Zcash Orchard, 2026-06), and a pipeline of near-misses. Liquid is the first fully-exploited consensus bug in the corpus since 2010 and the first outside Bitcoin Core itself, which makes it the anchor the Technique's promotion criteria were written for. The two full-exploit anchors rhyme structurally — an arithmetic/validation defect in the client that defines consensus, producing coins that violate the supply invariant — and diverge in a way worth recording: in 2010 the fraudulent 184.5 billion BTC was visible to anyone who read the block, and the fix landed in about five hours. In 2026 the fraudulent supply was cryptographically hidden, the bug was diagnosed from the outside, and the money was gone before anyone framed the question.

What generalises beyond Liquid is the layering error, not the cache bug. Liquid's public security story is a federation: hardware modules, geographic distribution, a signing threshold. Everything in that story was intact. The federation was never validating whether the sidechain's money supply was sound — it was validating that a request was well-formed and whitelisted, and inheriting soundness from consensus. Any system with this shape — a signing quorum, bridge committee, or attestation set that acts on a request whose validity was decided upstream — has the same property: the quorum's guarantee is capped by the weakest check it does not perform. Contributors documenting incidents in this class should record, explicitly, which invariant the signers checked and which one they assumed, because the gap between those two is where the loss lives.

The patch story deserves its own note for the T9.014 record, and it is a correction to the obvious version of it. A commit touching the vulnerable cache sat in the upstream tree for 34 days, its cherry-pick merged three hours after the money left, and the deployed release predated it by four months — which is a complete and compelling account of the incident that the forensic analysis then took apart, because that commit added fields without protecting their boundaries and the attack ran through the boundaries. The deployment gap is real and remains the structural problem: for a chain whose consensus is one client, "the fix exists" and "the fix is running" are separated by an interval an attacker can read off a public repository. But "a fix exists" is a claim about the exploited path, and it needs checking against that path. Contributors reconstructing a patch timeline should record what a commit actually changed, not only when it landed — the first is what decides whether an incident was preventable, and it is the part that gets skipped.

Techniques demonstrated (4)