Liquid Network's 4,000 BTC Exploit: How a Rangeproof Cache Key Flaw Unlocked $320 Million in Unbacked LBTC
An attacker exploited a flaw in Elements' rangeproof-verification cache — the cache key included the proof bytes but not the Pedersen commitment — allowing a proof verified for a small legitimate amount to satisfy the check for a commitment encoding 4,000 LBTC. The unbacked LBTC was redeemed through SideSwap's peg-out service for approximately $320 million in real BTC from Liquid's federation reserves. SideSwap's authorization key was not compromised. After negotiations, 3,400 BTC (~85%) was returned. Blockstream's September 23 assessment confirmed approximately 602 BTC remained outstanding and did not characterise that balance as an agreed bounty.
This article is published for educational and informational purposes only. It does not constitute legal, financial, or investment advice. Nothing in this article should be relied upon as the sole basis for any decision relating to the security, investment value, or legal standing of any protocol or digital asset.
Information published on any code vulnerabilities must not be used to attack, test, or probe any protocol or system without explicit authorisation from its owner. Use of this content is subject to our Terms of Service and Privacy Policy.
What happened
On September 6, 2026, an attacker exploited a flaw in Elements' rangeproof-verification cache to create approximately 4,000 unbacked LBTC on the Liquid Network, Blockstream's Bitcoin sidechain. Those tokens were then redeemed through SideSwap's peg-out service, converting them to real Bitcoin drawn from Liquid's federation reserves. The total withdrawn was approximately $320 million in BTC at prevailing prices.
Liquid's initial security announcement confirmed the withdrawal, disabled bridge nodes, and asked exchanges to pause LBTC deposits and withdrawals while the team investigated. A critical detail in the announcement: SideSwap's peg-out authorization key was not compromised. The attacker did not steal a key or forge a signature. They exploited a verification flaw that caused the node to accept unbacked LBTC as legitimate, and SideSwap's peg-out service processed the redemption in good faith.
The attacker left an on-chain message claiming to be whitehats and inviting Blockstream to contact them on-chain. As reported by Bitcoin Magazine and Protos, the claim was unverified at the time of the withdrawal and the funds had not been returned while initial coverage was being filed.
Following negotiations, the attacker returned 3,400 BTC, approximately 85% of the amount taken. In its September 23 incident assessment, Blockstream stated that approximately 602 BTC remained outstanding and that recovery efforts continued. Blockstream's assessment did not characterise the outstanding balance as an agreed bounty. CoinSpeaker noted criticism of the framing used in some coverage that had described the retained BTC as a self-awarded reward. The assessment also documented the patch, staged network recovery, and resumption of peg-in and peg-out operations after the fix was deployed.
Root cause
Liquid is built on Elements, Blockstream's open-source sidechain framework. Elements uses Confidential Transactions: output amounts are hidden behind Pedersen commitments, and each output carries a rangeproofthat proves in zero-knowledge that the committed value falls within a valid range. Without a valid rangeproof, a transaction could create outputs with negative or overflowing amounts, violating the conservation-of-value property the protocol depends on. Rangeproof verification is therefore the mathematical gatekeeper for every Confidential Transaction processed by a Liquid node.
The verification cache and its flaw
Rangeproof verification is computationally expensive. To avoid re-verifying the same proof on every node restart or block revalidation, Elements maintains a verification cache: a map from a proof identifier to a pass or fail result. The flaw was in how that cache key was constructed. The cache key was derived from the rangeproof bytes alone, without binding it to the specific Pedersen commitment the proof was supposed to validate.
This separation is the exploitable condition. A rangeproof proves that a particular commitment encodes a value in the valid range. If the cache key does not include the commitment, then a proof verified against commitment A (which commits to a small, legitimate value) can produce a cache entry that satisfies the verification check for commitment B (which commits to an arbitrarily large, fabricated value). The node looks up the proof hash, finds a prior valid result, and skips the actual mathematical verification of the new commitment.
// Confidential Transaction output:
// commitment = Pedersen commitment to amount (hides the value)
// rangeproof = zero-knowledge proof: committed value is in [0, 2^64]
//
// Correct cache key: hash(rangeproof_bytes || commitment_bytes)
// Ensures a proof is only valid for the specific commitment it was made for.
//
// Flawed cache key: hash(rangeproof_bytes) only
// A proof verified against commitment_A is cached under proof_hash.
// A later lookup for (proof_hash, commitment_B) hits the same cache entry.
bool VerifyRangeproof(const Commitment& commitment, const Rangeproof& proof) {
uint256 cache_key = Hash(proof.bytes); // [BUG]: commitment not included
if (rangeproof_cache.count(cache_key)) {
return rangeproof_cache[cache_key]; // cache hit: skips verification
}
bool result = secp256k1_rangeproof_verify(proof, commitment);
rangeproof_cache[cache_key] = result;
return result;
}
// Attacker's sequence:
// 1. Submit transaction T1 with commitment_small (e.g. 1 LBTC) and valid proof_P.
// Node verifies: proof_P is valid for commitment_small. Cached under Hash(proof_P).
//
// 2. Submit transaction T2 with commitment_large (e.g. 4,000 LBTC) and the same proof_P.
// Node looks up Hash(proof_P): cache hit, returns true. Skips verification.
// 4,000 unbacked LBTC is minted without a corresponding deposit.From unbacked LBTC to real BTC
The second step of the exploit used a legitimate part of the Liquid ecosystem. SideSwap provides a peg-out service that accepts LBTC and releases the corresponding amount of real BTC from the federation's on-chain reserves. SideSwap's peg-out authorization key was not compromised; the service processed the redemption because the LBTC the attacker presented appeared valid to any node running the flawed verification code. The federation released approximately 4,000 BTC to the attacker in exchange for LBTC that had no genuine backing.
// Step 1: Seed a legitimate transaction to populate the cache
submit_tx(
outputs = [{ commitment: commit(1 LBTC), rangeproof: valid_proof_P }]
)
// Node verifies valid_proof_P against commit(1 LBTC). Cache: Hash(valid_proof_P) = true.
// Step 2: Fabricate a large LBTC balance using the cached proof
submit_tx(
outputs = [{ commitment: commit(4000 LBTC), rangeproof: valid_proof_P }]
)
// Node looks up Hash(valid_proof_P): cache hit. Skips verification.
// 4,000 LBTC credited to attacker's Liquid address. No real BTC deposited.
// Step 3: Peg-out through SideSwap
sideswap.peg_out(amount = 4000 LBTC, destination = attacker_bitcoin_address)
// SideSwap holds valid LBTC (to its knowledge) and releases 4,000 BTC from federation.
// Federation BTC reserves: −4,000 BTC
// Attacker BTC wallet: +4,000 BTC (~$320 million)Why the peg-out service was not at fault
The peg-out path behaved exactly as designed. SideSwap received what appeared to be a valid LBTC balance, and the federation released the corresponding BTC. The vulnerability existed entirely upstream, at the point where the node accepted the fabricated LBTC as legitimate. Once the flawed verification passed and the unbacked balance was recorded, every downstream component, including the peg-out service, operated correctly from its own perspective. This is a general property of exploits that corrupt state at the validation layer: they do not require any other component to misbehave; the damage is baked in at the moment verification is bypassed.
What could have been done
- Bind cache keys to the full (proof, commitment) pair, not the proof alone. The root cause is a single-line error in cache key construction. A rangeproof is a statement about a specific commitment. A cached verification result is only valid for the exact (proof, commitment) pair it was computed from. The cache key must include both. This is not an obscure cryptographic requirement; it is the minimal correctness condition for any result cache on a function that takes multiple inputs. In a system where cache hits bypass expensive cryptographic verification, this correctness condition is a security boundary.
- Apply independent security review to the verification cache specifically. The verification cache is security-critical infrastructure, not a performance optimisation with a bounded downside if it misbehaves. Any code path that produces a "verified" result without actually executing the verification deserves the same scrutiny as the verification itself. Cache logic in cryptographic validation routines should be reviewed against the question: under what conditions does this cache hit authorise something it was not originally computed for?
- Set automated circuit breakers on large or anomalous peg-outs. The attacker redeemed approximately 4,000 LBTC in a single or small number of peg-out transactions. A real-time monitor that flags and pauses peg-out requests above a defined threshold relative to normal daily volume, or that requires additional authorisation for large single-transaction peg-outs, would have produced an intervention window before the full 4,000 BTC left the federation. The peg-out service itself did not have a way to know the LBTC was unbacked, but anomalous volume is detectable regardless.
- Monitor federation reserve outflows in real time against expected peg activity. The federation's BTC reserves are on-chain and fully observable. An automated system comparing actual reserve outflows against the historical rate of legitimate peg-out volume would have flagged a deviation of the magnitude seen here immediately. This monitoring does not require understanding the exploit mechanism; it requires only the observation that the federation is releasing Bitcoin at an unusual rate.
- Use formal verification or property-based testing for cryptographic validation routines. Rangeproof verification has a formal specification: a proof is valid if and only if it demonstrates that the associated commitment encodes a value in the valid range. Property-based tests that generate (proof, commitment) pairs and verify that swapping the commitment invalidates the cached result would directly exercise the exact condition that was exploited. Fuzzing and property testing of validation caches is an underutilised practice in blockchain node implementations, and its absence in this code path is the immediate engineering gap that enabled the exploit.
- Establish a clear threshold and process for large peg-in and peg-out requests. Federation-model sidechains accept and release Bitcoin based on automated threshold logic applied to incoming LBTC. For exceptionally large redemptions, a manual review step or a time-lock before the release executes would give the federation operator a window to verify that the LBTC backing the request is genuine. The cost of this friction on legitimate large peg-outs is real but bounded; the cost of releasing 4,000 BTC against unbacked tokens is not.
Lessons for the industry
The Liquid exploit is qualitatively different from the authorization bypasses and oracle manipulation attacks that dominate the 2026 incident record. Those attacks exploit logical failures in access control or economic assumptions. This one exploited a flaw in cryptographic verification itself, specifically in the component of the system that is supposed to make fabrication mathematically impossible. The rangeproof is the boundary between "amounts must be real" and "amounts can be whatever the node accepts." A bug that bypasses rangeproof verification does not weaken a security check; it disables the mathematical guarantee that makes Confidential Transactions meaningful.
The cache poisoning pattern that enabled this exploit appears in other security domains under different names, but the underlying structure is consistent: a result computed in one context is reused in a different context where its validity no longer holds. In web application security, this manifests as session fixation and cached authorization state. In blockchain validation, it manifests as proof reuse across distinct commitments. The mitigation principle is also consistent: cached results must be scoped precisely to the inputs from which they were derived. For any function that takes (X, Y) and produces a security-relevant result, a cache keyed only on X is a potential vulnerability whenever Y can vary.
The "whitehat" episode and its aftermath warrant separate attention. The attacker claimed benign intent in an on-chain message, returned 3,400 BTC through negotiations, and retained approximately 602 BTC. Blockstream's September 23 assessment does not characterise the retained amount as an agreed bounty. This distinction matters for how the industry interprets these events. A negotiated outcome in which most funds are returned and a portion is retained creates ambiguous incentives: it rewards the identification and exploitation of vulnerabilities with an uncertain but potentially significant payout, without the accountability structure of a formal bug bounty programme. The alternative, no return at all, is clearly worse. But the framing of a self-determined "reward" extracted through exploitation is not equivalent to a responsible disclosure, and treating it as such erodes the boundary between security research and economic exploitation.
For federation-based sidechains broadly, this incident adds to the evidence that the federation model concentrates risk in ways that are not always visible until a single flaw is exploited. The Liquid federation holds real Bitcoin on behalf of LBTC holders. That reserve is only as secure as the validation logic that governs what counts as legitimate LBTC. A single implementation bug in that validation logic translated directly into a 4,000 BTC exposure. The federation architecture provides recovery mechanisms, coordination capability, and human judgment that a fully automated system lacks. But it does not reduce the attack surface of the node software itself. Investing in the security of that software, through rigorous testing of validation caches, independent audits of verification logic, and property-based testing of cryptographic routines, is proportional to the value the federation holds, not to the sophistication of the last known attack.
The staged recovery Blockstream executed after patching, resuming peg-in and peg-out operations incrementally rather than in a single restart, reflects a measured approach to restoring trust in a system whose state had been corrupted. That approach is worth preserving as a model. When a validation-layer flaw is patched, the question is not only whether the flaw is fixed but whether any residual state introduced during the exploit window can be identified and isolated. A staged recovery that monitors each resumption phase for anomalies before proceeding is significantly safer than a full restart that assumes the patched code is sufficient without further verification of the recovered state.
- 01Blockstream, Liquid Network Security Incident Assessment
- 02Liquid Network on X, Liquid Network initial security announcement
- 03Bitcoin Magazine, Alleged white hats withdraw 4,000 BTC from Blockstream's Liquid Network federation reserves
- 04Protos, How 4,000 BTC left Liquid Network
- 05Cointelegraph, Liquid pauses after $320 million withdrawal
- 06CoinSpeaker, 3,400 BTC returned, about 598.5 BTC still held