During the past week (2026/09/07 - 2026/09/13), we observed 2 security incidents with a total estimated loss of approximately $320M.
| Date | Incident | Type | Estimated Loss |
|---|---|---|---|
| 2026/09/06 * | Liquid Network | Flawed Cache Key Construction | ~$320M |
| 2026/09/11 | Symbiosis | Flawed Off-Chain Deposit Validation | ~$770K |
*The Liquid Network incident occurred on September 6 and was not covered in last week's report. It is included here for completeness.
Best Security Auditor for Web3
Validate design, code, and business logic before launch
Weekly Highlight: Liquid Network
The Liquid Network incident was selected for its subtle cache-key collision mechanism and the substantial losses it caused. By crafting a transaction whose validation data re-divided the bytes of an earlier one, the attacker had a cached validation result returned for a proof that was never checked.
On 2026/09/06, the Liquid Network, a Bitcoin sidechain, was exploited for approximately $320M [1]. A collision in the rangeproof validation cache of Elements, the node software Liquid runs, allowed a valid verdict recorded for one output to be returned for a second, entirely different output, whose proof was therefore accepted without ever being examined. The attacker used this to create 4,000 L-BTC that no bitcoin backed, then withdrew them through the network's ordinary peg-out. The following day 3,400 BTC was returned to the federation, leaving about 598.5 BTC with the attacker [2].
Background
The Liquid Network is a Bitcoin sidechain built on Elements, an open-source blockchain platform derived from the Bitcoin codebase. It exists to move bitcoin faster, more cheaply and more privately than the main chain allows. Bitcoin crosses between the two networks through a two-way peg: in a peg-in, a user locks BTC with the network's federation, a fixed group of vetted organizations that jointly custody the peg funds, and receives an equal amount of L-BTC; in a peg-out, the user destroys L-BTC and the federation releases the corresponding BTC. Liquid blocks are not mined. A rotating set of the federation's functionary nodes proposes them, and a threshold of federation signatures finalizes them.
Like Bitcoin, Liquid tracks funds as unspent transaction outputs rather than as account balances. A transaction names existing unspent outputs, unlocks them, and creates new ones, and the value it creates must equal the value it consumes. Every output carries a locking script, its scriptPubKey. An output whose script begins with OP_RETURN can never be spent; it exists to carry data, and a peg-out is expressed as exactly that kind of output.
Liquid also hides amounts by default. Rather than writing a figure into an output, it writes a Pedersen commitment to that figure. Commitments are additive, so a node can confirm that a transaction's inputs and outputs balance without learning any of the values involved. That property cuts both ways: a commitment can just as well hide a value that behaves as negative, which would let a transaction's outputs exceed its inputs and still balance. Every output whose amount is hidden therefore carries a rangeproof, a proof that the committed amount lies within [0, 2^64). Verifying a rangeproof is expensive, and the same output is verified more than once — when the transaction enters the mempool, and again when it arrives in a block — so Elements keeps a cache of the proofs it has already accepted, keyed by the proof and the data it was checked against.
Vulnerability Analysis
The defect is in how Elements, the node software every Liquid participant runs, caches already-verified rangeproofs.
CachingRangeProofChecker::VerifyRangeProof() derives a cache entry for the output in front of it and, on a hit, returns success immediately without touching the proof:

The entry comes from ComputeEntryRangeProof(), which writes four fields one after another into a single SHA-256 stream and finalizes the digest:

The four fields are the rangeproof itself, the Pedersen commitment to the amount, the asset generator that identifies which asset the output holds, and the scriptPubKey, included so that an accepted proof binds to one specific locking script.
None of the four fields carries a length prefix or a separator. The rangeproof and the scriptPubKey vary in length; the Pedersen commitment and the asset generator are always 33-byte points. The digest therefore records only the concatenated bytes, with nothing to mark where one field ended and the next began. Two different sets of four fields that happen to concatenate to the same byte stream produce the same entry, and whichever is validated first leaves behind a verdict the other one collects. The hasher is seeded with a per-node random salt, but the salt is prefixed to both streams alike, so it changes the digest without distinguishing the two inputs. The defect was remediated in commit 94000967 [3].
Attack Analysis
The following analysis is based on the transactions 271147...187ec5 and f24a4b...0a183f, both accepted by nodes running the vulnerable code.
- Step 1: The attacker published a transaction whose first output is an
OP_RETURNcarrying 67 bytes of data.
Its scriptPubKey reads 6a 43 — the OP_RETURN opcode followed by a push of 67 bytes — then two 33-byte values and a trailing 6a. Those 67 pushed bytes are not data this transaction needs. They are the value commitment and the asset generator of an output that did not exist yet. Validating this output stored a valid verdict under the entry for its own four fields.
- Step 2: The attacker then submitted the inflation transaction. Its
OP_RETURNoutput has ascriptPubKeyconsisting of the single byte6a, and its value commitment is exactly the 33-byte value embedded in the earlier transaction's push. Its rangeproof field was built from the earlier transaction's rangeproof, commitment and asset generator, followed by the two bytes6a 43.

The four fields of this output therefore concatenate to the same byte stream as the first transaction's, re-divided along different boundaries:
Primer: P0 │ C0 │ X │ S0, where S0 = 6a 43 <33-byte C1> <33-byte X> 6a
Inflation: P1 │ C1 │ X │ S1, where P1 = P0 ‖ C0 ‖ X ‖ 6a 43 and S1 = 6a
Both concatenate to: P0 ‖ C0 ‖ X ‖ 6a 43 ‖ C1 ‖ X ‖ 6a
The cache returned the earlier verdict, and P1 was never verified — it is not a rangeproof at all. C1 commits to a small negative amount. The transaction's other output, a normal pay-to-witness-public-key-hash output to the attacker's address ex1q7kg...qa2w, carried a large positive commitment with a genuine rangeproof, and the two offset each other, so the transaction balanced. It left 4,000 unspent L-BTC that no bitcoin backed.
- Step 3: The attacker pegged out. Two transactions burned
3,996.01834922and2.65138358L-BTC through ordinaryOP_RETURNpeg-out outputs,3,998.66973280L-BTC in total. The functionary nodes that authorize peg-outs validated them with the same vulnerable code and released the bitcoin:3,995.99999857BTC and2.49749857BTC reached the attacker on Bitcoin,3,998.49749714BTC in all.
The network paused and split into two chains while the incident was resolved [4], and resumed with the invalid peg-out rejected [2]: a block explorer today shows the primer transaction confirmed but not the inflation transaction, even though the bitcoin it released had already left the federation's wallet. At 16:09:25 UTC the next day, the attacker returned 3,400.00000000 BTC to the federation's peg wallet, keeping the remaining ~598.5 BTC outstanding [2], as the two sides negotiated through messages embedded in Bitcoin transactions [4].
Conclusion
The attacker broke no cryptography and compromised no key. The defect was in how the rangeproof cache identified what it had already checked. Its key concatenated four fields — two of them variable in length — into one hash stream with nothing marking where each field ended, so a second set of fields could re-divide the same bytes and land on the same entry. A stored valid verdict was then returned for a proof no node had ever verified, and once a rangeproof stops constraining the amount behind it, a confidential ledger loses the only thing keeping its hidden values non-negative. One crafted output became 4,000 L-BTC, and the two-way peg turned them into bitcoin on the main chain.
Any identifier derived from more than one variable-length input has to commit to the boundaries between them, whether by prefixing each field with its length or by hashing the fields into separate fixed-size slots. Without that the derivation is not injective, and two distinct inputs can be mistaken for one. The requirement is sharpest wherever a cache stands in for a check, because there a hit is a decision not to run it.
More Incidents This Week
Symbiosis
On 2026/09/11, the Bitcoin route of the Symbiosis cross-chain bridge was exploited across its BNB Smart Chain, Ethereum and Rootstock deployments, costing liquidity providers and users an estimated 9.97 BTC (~$770K, at the ~$77K September-11 price) [5]. Off-chain code that decides how much synthetic bitcoin to mint from a Bitcoin deposit took the depositor's identity from a part of the transaction the depositor controls, letting the attacker pass as the administrator and push the minimum fee below zero; it then subtracted that fee from the deposit without checking its sign, so the subtraction added.
Background
Symbiosis is a cross-chain liquidity protocol that moves value between chains by locking and minting rather than by transferring the asset itself: on chains with smart contracts, a user's tokens are locked by the Portal contract on the source chain, and the Synthesis contract mints an equal amount of a synthetic token, an sToken, on the host chain; burning the sToken releases the original [6]. Bitcoin arrives through this route as syBTC, a token with 8 decimals matching bitcoin's own smallest unit, issued on BNB Smart Chain, Ethereum and Rootstock and paired in pools against BTCB, cbBTC, WBTC and RBTC [5].

Bitcoin has none of that: no smart contracts, so no Portal contract to lock deposits. In its place is the portal — a designated Bitcoin address that plays the same role. A deposit is an ordinary Bitcoin transaction paying it, with the instructions for the destination chain attached to it as data. Off-chain code belonging to the bridge reads that data to learn who is depositing, how much, and where the synthetic token should go, and enforces the portal's fee — subtracted from the deposited amount before the amount to mint is fixed — whose minimum is a parameter its administrator sets.
Nothing about the Bitcoin side is visible to the destination chain, so a Relayers Network carries the resulting request across. The request arrives at BridgeV2, the bridge's on-chain entry point, as an encoded call that it forwards to Synthesis. Authorization rests on an MPC key: the protocol's signers hold shares of a single private key and jointly produce one signature over the request. The entry point for relayed requests carries a single modifier and does nothing else before dispatching the call:

That modifier hashes the request and requires the accompanying signature to be valid for the MPC address:

What the contract establishes is that the MPC key signed the request. The amount the request carries is computed on the Bitcoin side.
Vulnerability Analysis
The defect is not in the destination-chain contracts. It is in the off-chain service that reads Bitcoin deposits and turns them into cross-chain requests. Its source is not publicly available, so the account below follows the team's post-mortem [5] together with a 2024 third-party audit report that contains a code snippet which appears related [7].
Two flaws in the deposit path had to line up, and the post-mortem states that neither was sufficient on its own.
The first is in how the depositor is identified. The instructions attached to a deposit are decoded to recover who is making it, and the decoder took that identity from a part of the transaction data that whoever spends the input controls. A depositor could therefore name themselves as any party the protocol recognizes, including the portal's administrator, the role that sets the minimum fee the portal will accept.
The second is in how that fee is applied. The 2024 audit reported the line in decodeWrap() that computes the amount to mint by subtracting the fee from the value of the deposit output:
Value: types.Satoshi(tx.TxOut[idx].Value) - info.PortalFee,
Its finding was that types.Satoshi is a signed integer type while the info structure carrying PortalFee is untrusted and user-controlled, so the result can be driven negative and, serialized for the destination chain as an unsigned value, becomes large enough to mint an arbitrary quantity of synthetic bitcoin. The audit recorded the issue as fixed at the time [7]. The 2026 post-mortem describes the same kind of failure: the fee was subtracted from the deposit without a check on its sign [5], so a fee below zero enlarged the deposit rather than reducing it. Nothing in the deposit path appears to bound the amount to mint by the bitcoin actually received.
Attack Analysis
Twelve forged mints went through across BNB Smart Chain, Ethereum and Rootstock in roughly four minutes [5]. The following analysis is based on one of them, the transaction 0x9a2bc0...21b9b959 on BNB Smart Chain.

-
Step 1: Passing as the portal's administrator, the attacker moved the minimum fee the portal accepts below zero, so that a deposit declaring a negative fee would be processed instead of rejected [5].
-
Step 2: The attacker deposited 330 satoshi, which appears as
0x000000000000014a. The request that reached BNB Smart Chain carried0x400000000000014a— the same value with the bit for2^62set,4,611,686,018,427,388,234base units. The difference between the two is exactly2^62, so the fee subtracted from this deposit was-4,611,686,018,427,387,904satoshi:330 - (-4,611,686,018,427,387,904) = 4,611,686,018,427,388,234The request was signed and relayed in that form.
-
Step 3:
receiveRequestV2Signed()verified the MPC signature over the request and passed it on, reachingmetaMintSyntheticTokenBTC(). That function resolved the synthetic representation of the real token for the source chain ID,syBTC, and calledsynthesize()for the full signed amount. All of it was transferred to the attacker's address0x025122...5d3Ba2:46,116,860,184.27388234 syBTC. -
Step 4: The attacker sold the synthetic token into the pools holding it. A single swap through a Uniswap v4
syBTC/WBTCpool settled18,446,744,072,845,450,682base units ofsyBTC, about four times what the transaction above minted, and took out4.38897292 WBTC.

Routed onward, the proceeds of that swap came to roughly 137 ether.
The team paused the Bitcoin route, and about 15.2 BTC of portal funds were moved to reserve addresses within hours [5]. A bounty of 20% of anything returned was offered, open until September 13 [8]. The protocol's other routes were unaffected.
Conclusion
No key was stolen and no contract was tricked into running code it was not written to run. The signature the destination chain checked was genuine; what it authorized was a number that off-chain code had already produced incorrectly in two separate ways: once when it read the depositor's identity from a field the depositor controls, letting the attacker pose as the portal's administrator and push its minimum fee below zero, and again when it subtracted that fee from the deposit without checking its sign, so a negative fee enlarged the deposit instead of reducing it.
An identity read out of untrusted input is not an identity: a privileged role can only be established by something the depositor cannot choose, such as the address that actually authorized the spent input, checked against a list the protocol maintains — not a field the depositor is free to fill in. A value that is subtracted needs bounds on both sides: a fee should be required to fall between zero and the deposit, and the amount to mint should never be permitted to exceed what the source chain actually received. Either bound would have stopped this on its own.
References
[1] https://x.com/Liquid_BTC/status/2096696272447218108
[2] https://x.com/Liquid_BTC/status/2097404704028545175
[3] https://github.com/ElementsProject/elements/commit/94000967f6dc05b1afd435e79b1bbc597e29f816
[4] https://www.coindesk.com/markets/2026/09/08/white-hat-hackers-return-most-of-usd320m-bitcoin-taken-from-liquid-network
[5] https://x.com/symbiosis_fi/status/2099566361940795831
[6] https://docs.symbiosis.finance/crosschain-liquidity-engine/synthesizing-process
[7] https://github.com/symbiosis-finance/audits/blob/master/Symbiosis%20Relayers%20Network/Symbiosis%20Relayers%20Network%202024%20-%20Decurity.pdf
[8] https://x.com/symbiosis_fi/status/2098442463358718264
About BlockSec
BlockSec is a full-stack blockchain security and crypto compliance provider. We build products and services that help customers to perform code audit (including smart contracts, blockchain and wallets), intercept attacks in real time, analyze incidents, trace illicit funds, and meet AML/CFT obligations, across the full lifecycle of protocols and platforms.
BlockSec has published multiple blockchain security papers in prestigious conferences, reported several zero-day attacks of DeFi applications, blocked multiple hacks to rescue more than 20 million dollars, and secured billions of cryptocurrencies.
-
Official website: https://blocksec.com/
-
Official Twitter account: https://twitter.com/BlockSecTeam



