Liquid Network Incident Analysis

研究与成果 安全事件分析
Liquid Network Incident Analysis

Incident Summary

On 6 September 2026, the Liquid Network was exploited through a vulnerability in the Elements codebase used to validate Confidential Transactions. The issue stemmed from an ambiguous cache-key encoding in the rangeproof verification cache, allowing two different validation inputs to produce the same cached entry. The attacker first crafted setup transactions and primed the cache, then submitted a malicious inflation transaction that reused the cached validation result and created unauthorized L-BTC. Approximately 3,998.5 L-BTC, valued at $318.7 million at the time of the exploit, was affected, with the attacker subsequently pegging out the assets into native Bitcoin. liquid1

Background

Liquid is a Bitcoin sidechain built on Elements. BTC locked in the Liquid Federation’s Bitcoin wallet backs L-BTC issued on the sidechain. Under the intended peg model, one L-BTC represents one BTC held by the federation.

Elements supports “Confidential Transactions” and “Confidential Assets”. Instead of publishing an amount directly, an output can contain a Pedersen value commitment. An asset may also be represented by a commitment. The transaction’s commitment equation establishes that inputs and outputs balance algebraically, while a ‘rangeproof’ demonstrates that each hidden output value lies in an allowed nonnegative range.

In particular, rangeproof validation is part of ensuring that a confidential amount is both legitimate and cannot be abused to create value outside the permitted range. Actual validity also depends on: rangeproof + value commitment + asset commitment + scriptPubKey. codeliquid https://github.com/ElementsProject/elements/blob/elements-23.4.0rc3/src/script/sigcache.cpp?utm_source=chatgpt.com

The script is supplied to secp256k1_rangeproof_verify as additional committed data. A proof valid for one (C, X, S) tuple is not automatically valid for a different tuple.

This distinction is essential. Pedersen commitments operate algebraically and can balance a very large positive output against a negative value. The rangeproof is what prevents that negative value from being accepted as a legitimate output. Bypassing the rangeproof check therefore turns an otherwise balanced commitment equation into an inflation mechanism.

Key Transactions (Times in UTC)

Timestamp Transaction / Explanation
2026-09-06 13:52:10 https://blockstream.info/liquid/tx/271147100a94f6337b6c3db39b30c92d5b97ed91597307b6f721f73a15187ec5 Set up
2026-09-06 13:53:10 https://blockstream.info/liquid/tx/f24a4b179b5cc7e88b25a763911f7cbdf2bf45d1d1b5ab611e94461cef0a183f Liquid-side inflation transaction
2026-09-06 13:54:10 https://blockstream.info/liquid/tx/3875a6d6ed4af708e6fd90d1c5504dc014e52c7093a566987252006d6cf1146b?input:0&expand Consumes an incident-derived UTXO and creates new confidential Liquid output(s) controlled by the attacker/next-hop wallet
2026-09-06 14:00:10 https://blockstream.info/liquid/tx/c6ea588ac26f5838b6acbb2a444a33b325bbfeb39bf16dfe27b47215ffd72267 Consumes an incident-derived UTXO and creates new confidential Liquid output(s) controlled by the attacker/next-hop wallet
2026-09-06 14:01:10 https://blockstream.info/liquid/tx/46f117c990580501a5156937a8c8affda551a38b3c0b99d29eb8469ee6beb3d2 Peg-out 2.65138358 LBTC
2026-09-06 14:06:10 https://blockstream.info/liquid/tx/ce4caece413cd9d444ce7ed9f54e5b328b3da5e4af301aff59a3571f76e988f2 Peg-out 3,996.01834922 LBTC
2026-09-06 14:28:56 https://blockstream.info/tx/85d2ca15bea33a592e73ed40c6a5da887feecf1e77f58ec7f580e00841645043 Sent 3,996.01834922 BTC to bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte
2026-09-07 18:09:25 https://mempool.space/tx/a6d697a25266ce3c78774fd1d75f896b7af522ada209b0f6228ea497bc49a46d Returned 3400 BTC to bc1qdlld6antmv4xug242ed83q7k4rqw50cwfns38szx4qu2f4jwaxxsuhwxxr

Key Addresses

Address Label
ex1q7kgx4ptje7px48tn0nsmc6se5pngdp3smpqa2w Attacker on Liquid
bc1qgslsydz56d0ed6827hdemfmk5w2f6ldyc6wt7p Recipient of the 3,996.01834922 BTC federation payout
bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte Recipient of the 2.65138358 BTC federation payout
bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte Attacker consolidation and on-chain messaging address
bc1qdlld6antmv4xug242ed83q7k4rqw50cwfns38szx4qu2f4jwaxxsuhwxxr Liquid Federation Bitcoin peg wallet

Attack Flow

1. Forge the proof Cache

We fetched and parsed the raw Elements serialization rather than relying only on the explorer’s rendered fields. The :0 outputs of the two setup transactions are identical across their asset, value commitment, nonce, script, surjection proof, and rangeproof.

Using zero-based end-exclusive offsets in the 8,882-byte raw transaction to decode output_0 of transaction 271147100a94f6337b6c3db39b30c92d5b97ed91597307b6f721f73a15187ec5 returned the following: liquid3

a. C0 = 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5 b.S0=6a|43|086f5d67160fc4b477954fb09ef321e5b589d7a07740a1a6df494ed2335b1d01d8|0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d|6a.

i.6a means OP_RETURN ii.43 means push the following 67 bytes The 67 pushed bytes are deliberately structured as: 33-byte C1, 33-byte X, 1-byte 6a
liquid4 Therefore Script S0 = 6a43 || C1 || X || 6a where X = 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d is the result of secp256k1_generator_generate(L-BTC asset ID).

2. Prime the live mempool cache

Block connection uses cache semantics that consume an existing entry and do not store a new one on a miss. As a result, connecting block 4,050,335 should remove the cache entry produced when the two visible setup transactions were first accepted. The inflation transaction in block 4,050,336 therefore requires a fresh valid transaction containing the same (P0, C0, X, S0) tuple to reach each accepting node after block 4,050,335 was connected and before the invalid output was checked.

That live primer txid and raw bytes remain unknown; identifying it may require a functionary debug.log, a contemporaneous mempool.dat, a peer-relay capture, or an archived mempool snapshot.

3. Liquid side inflation

Decoding output_1 of inflation transaction f24a4b179b5cc7e88b25a763911f7cbdf2bf45d1d1b5ab611e94461cef0a183f returned the data below: liquid5 a. C1 = 086f5d67160fc4b477954fb09ef321e5b589d7a07740a1a6df494ed2335b1d01d8 This is exactly the first 33 bytes of data pushed by the setup output’s OP_RETURN script. *Note that the rangeproof P1 is 68 bytes longer than P0, specifically at the end 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5

b. 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d6a43 which gives C0 from setup: 09d6c6150e95e576d288dcc890bf48518119c6f227ea1be6f9a932792dbee683f5 and X = 0a0a488de4899d0ae757f6cf8368663184d164106111ed9eaecf510e35282ddc6d is the result of secp256k1_generator_generate(L-BTC asset ID). Embedding the precise L-BTC generator allowed the setup script’s bytes to realign with the attack output’s asset-generator field.

c. Overrall

Valid setup: P0 || C0 || X || (6a43 || C1 || X || 6a)

Invalid attack: (P0 || C0 || X || 6a43) || C1 || X || 6a

Both reduce to the same 4,301-byte stream: P0 || C0 || X || 6a43 || C1 || X || 6a Independent verification with rust-elements and secp256k1-zkp produced the following results: The cache lookup occurs before commitment parsing and secp256k1_rangeproof_verify. A hit returns true immediately, so the invalid P1 is never cryptographically checked on a primed node liquid6

4. Cash out

L-BTC is redeemable against real BTC controlled by Liquid's federation. The next transaction, 3875a6d6…f1146b, spent the incident transaction’s output f24…:0. Two later transactions created explicit sendtomainchain peg-out outputs:

  • 46f117c9…eb3d2: 2.65138358 L-BTC
  • ce4caece…988f2: 3,996.01834922 L-BTC

Bitcoin transaction 8db751a6…7b140, confirmed in block 965,783, paid the two exact incident peg-out amounts: liquid7

Vulnerability

Elements caches successful rangeproof verifications to avoid repeating an expensive cryptographic operation. Two key constructions are relevant.

Earlier code derived the cache entry from only: liquid8 That design omitted the asset generator and script even though both affect proof validity. Commit c26d719 attempted to bind the cache entry to the full verification context. It changed the input to: liquid9 However, the implementation fed the raw fields into one SHA-256 stream without encoding their lengths. Both P and S are variable-length fields. Consequently, distinct tuples can produce the same byte stream by moving bytes across field boundaries. Patched cache implementation. liquid10 Valid primer: (P0, C0, X, 6a43 || C1 || X || 6a)

Invalid attack: (P0 || C0 || X || 6a43, C1, X, 6a)

The observed setup and attack outputs do not collide under the earlier P || C key. Their old-key inputs differ in both length and content. The construction specifically matches the four-field, undelimited key introduced by the patch. Acceptance therefore points to a c26d719-like implementation combined with a primed cache, although the exact binaries deployed on individual functionaries have not been published.

Fund Flow

The self-proclaimed white hat returned 3,400 BTC (268 million dollars) and kept 598.5 BTC (47 million dollars), 15% of the total, as a bounty reward. bc1ql4mfu6aundtkksxklfajs2h3t9nzcd6gyqjlte. liquid11

To keep up to date on the latest incident alerts and statistics, follow @certikalert on X, or read our latest analysis on certik.com.

相关博客

CertiK and Liquid’s Official Partnership

CertiK and Liquid’s Official Partnership

CertiK is excited to partner with Liquid to provide the cybersecurity expertise they seek.

CertiK Ventures Announces Investment in Zoo Finance

CertiK Ventures Announces Investment in Zoo Finance

CertiK Ventures is proud to announce our investment in Zoo Finance – a DeFi protocol pioneering the next evolution of blockchain fundraising via its Liquid Node Token (LNT) architecture.