在过去一周(2026/09/07 - 2026/09/13),我们观察到 2 起安全事件,估计总损失约为 3.2 亿美元。
| 日期 | 事件 | 类型 | 估计损失 |
|---|---|---|---|
| 2026/09/06 * | Liquid Network | 缺陷的缓存键构造 | ~$320M |
| 2026/09/11 | Symbiosis | 缺陷的链下存款验证 | ~$770K |
*Liquid Network 事件发生于 9 月 6 日,未在上周的报告中涉及。为完整起见,此处将其纳入。
Best Security Auditor for Web3
Validate design, code, and business logic before launch
每周焦点:Liquid Network
Liquid Network 事件之所以被选中,是因为其微妙的缓存键碰撞机制及其造成的巨额损失。通过构造一笔验证数据能够以不同方式重新划分早前交易字节的交易,攻击者使得系统为一个从未被检查过的证明返回了缓存的验证结果。
2026/09/06,比特币侧链 Liquid Network 遭到攻击,损失约 3.2 亿美元 [1]。Liquid 所运行的节点软件 Elements 中,其范围证明(rangeproof)验证缓存出现了一次碰撞,使得为某一输出记录的 valid(有效)判定结果被返回给了第二个完全不同的输出,因此后者的证明从未被真正检查便被接受了。攻击者利用这一点铸造了 4,000 枚没有任何比特币支持的 L-BTC,随后通过网络的常规 peg-out(锚定转出)机制将其提取。次日,3,400 枚 BTC 被归还给联盟(federation),攻击者手中仍留有约 598.5 枚 BTC [2]。
背景
Liquid Network 是构建在 Elements 之上的比特币侧链,Elements 是源自比特币代码库的开源区块链平台。它的存在是为了让比特币的转移比主链更快、更便宜、更具隐私性。比特币通过双向锚定(two-way peg)在两个网络之间流转:在 peg-in(锚定转入)中,用户将 BTC 锁定给网络的联盟——一组共同托管锚定资金的、经过审查的固定组织——并获得等量的 L-BTC;在 peg-out(锚定转出)中,用户销毁 L-BTC,联盟释放相应的 BTC。Liquid 的区块并非通过挖矿产生。联盟的一组轮值功能节点(functionary nodes)提出区块,并由达到阈值数量的联盟签名来最终确认。
与比特币一样,Liquid 以未花费交易输出(UTXO)而非账户余额的形式追踪资金。一笔交易指定已存在的未花费输出,将其解锁,并创建新的输出,其创建的价值必须等于其消耗的价值。每个输出都带有一个锁定脚本,即其 scriptPubKey。以 OP_RETURN 开头的脚本的输出永远无法被花费;它的存在是为了携带数据,而 peg-out 正是以这种类型的输出来表达的。
Liquid 默认还会隐藏金额。它并不将具体数字写入输出,而是写入对该数字的 Pedersen 承诺(Pedersen commitment)。承诺具有可加性,因此节点可以确认一笔交易的输入与输出是否平衡,而无需得知其中任何具体数值。这一特性是双刃剑:一个承诺同样也能够隐藏一个表现为负数的值,这将使得一笔交易的输出总额超过其输入总额而依然保持平衡。因此,每个金额被隐藏的输出都携带一个范围证明(rangeproof),用以证明所承诺的金额落在 [0, 2^64) 范围内。验证一个范围证明的代价高昂,且同一个输出会被验证不止一次——一次是在交易进入内存池(mempool)时,另一次是在其被打包进区块时——因此 Elements 会维护一个已接受证明的缓存,以证明本身及其所对照的数据作为键。
漏洞分析
该缺陷存在于 Elements(每个 Liquid 参与者都运行的节点软件)缓存已验证范围证明的方式中。
CachingRangeProofChecker::VerifyRangeProof() 会为其面前的输出推导出一个缓存条目,若命中,则立即返回成功,而不再触碰该证明:

该条目来自 ComputeEntryRangeProof(),它将四个字段依次写入单个 SHA-256 流中,并最终完成摘要计算:

这四个字段分别是范围证明本身、对金额的 Pedersen 承诺、标识该输出所持有资产的资产生成元(asset generator),以及 scriptPubKey——之所以包含后者,是为了使一个被接受的证明绑定到某个特定的锁定脚本。
这四个字段都没有携带长度前缀或分隔符。范围证明和 scriptPubKey 的长度是可变的;Pedersen 承诺和资产生成元始终是 33 字节的点。因此,摘要仅记录了拼接后的字节,而没有任何标记来区分一个字段的结束与下一个字段的开始。两组不同的四字段数据,只要恰好拼接成相同的字节流,就会产生相同的条目,而无论哪一组先被验证,都会留下一个供另一组收取的判定结果。哈希器使用了每个节点各自的随机盐值来播种,但该盐值同样被加在两个字节流的前面,因此它只会改变摘要值,却无法区分这两个输入。该缺陷已在提交记录 94000967 [3] 中得到修复。
攻击分析
以下分析基于交易 271147...187ec5 和 f24a4b...0a183f,二者均被运行存在漏洞代码的节点接受。
- 第一步:攻击者发布了一笔交易,其第一个输出是一个携带 67 字节数据的
OP_RETURN。
它的 scriptPubKey 读作 6a 43——即 OP_RETURN 操作码后跟一个 67 字节的压栈操作——随后是两个 33 字节的值以及末尾的 6a。这被压入的 67 字节并非这笔交易本身所需要的数据。它们是一个尚不存在的输出的价值承诺和资产生成元。对该输出进行验证后,系统在该输出自身四个字段对应的条目下存储了一个 valid 判定结果。
- 第二步:随后,攻击者提交了增发(inflation)交易。其
OP_RETURN输出的scriptPubKey仅由单个字节6a组成,而其价值承诺恰好等于前一笔交易压栈数据中所嵌入的那个 33 字节的值。其范围证明字段则是由前一笔交易的范围证明、承诺以及资产生成元构建而成,后面再跟上两个字节6a 43。

因此,这个输出的四个字段拼接后形成的字节流与第一笔交易的相同,只是沿着不同的边界重新划分:
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
缓存返回了之前的判定结果,而 P1 从未被真正验证过——它根本就不是一个范围证明。C1 承诺的是一个很小的负数金额。该交易的另一个输出——一个发往攻击者地址 ex1q7kg...qa2w 的普通 pay-to-witness-public-key-hash 输出——携带了一个数额很大的正承诺,并附有真实的范围证明,两者相互抵消,使得该交易得以保持平衡。这笔交易凭空产生了 4,000 枚没有任何比特币支持的未花费 L-BTC。
- 第三步:攻击者进行了 peg-out(锚定转出)。两笔交易通过普通的
OP_RETURNpeg-out 输出分别销毁了3,996.01834922和2.65138358枚 L-BTC,合计3,998.66973280枚 L-BTC。负责授权 peg-out 的功能节点使用同样存在漏洞的代码对其进行了验证,并释放了比特币:3,995.99999857BTC 和2.49749857BTC 到达了攻击者在比特币网络上的地址,合计3,998.49749714BTC。
在事件处理期间,网络暂停并分裂为两条链 [4],随后网络恢复运行,那笔无效的 peg-out 被拒绝 [2]:如今区块浏览器显示 primer 交易已被确认,而 inflation 交易并未被确认,尽管其所释放的比特币早已离开了联盟的钱包。次日 UTC 时间 16:09:25,攻击者向联盟的锚定钱包归还了 3,400.00000000 枚 BTC,剩余约 598.5 枚 BTC 仍未归还 [2],双方通过嵌入在比特币交易中的信息进行了协商 [4]。
结论
攻击者并未破解任何密码学机制,也未攻破任何密钥。缺陷出在范围证明缓存识别其已检查内容的方式上。其缓存键将四个字段——其中两个长度可变——拼接进同一个哈希流中,却没有任何标记来划分各字段的边界,因此第二组字段可以对相同的字节重新进行划分,并落在同一个条目上。于是,一个从未被任何节点真正验证过的证明,得到了系统所存储的 valid 判定结果的放行;而一旦范围证明不再对其背后的金额构成约束,一个机密账本便失去了保证其隐藏数值非负的唯一保障。一个精心构造的输出就此变成了 4,000 枚 L-BTC,而双向锚定机制又将其转变为主链上的比特币。
任何由多个可变长度输入派生而来的标识符,都必须对这些输入之间的边界作出承诺,无论是通过为每个字段加上长度前缀,还是将各字段哈希进各自独立的固定大小槽位中。若不这样做,该推导过程就不是单射的,两个不同的输入就可能被误认为是同一个。在缓存代替检查的场景中,这一要求最为严苛,因为在那里,命中就意味着决定不再执行检查。
本周更多事件
Symbiosis
2026/09/11,跨链桥 Symbiosis 的比特币路由在其 BNB Smart Chain、以太坊(Ethereum)和 Rootstock 部署上遭到攻击,导致流动性提供者和用户损失约 9.97 BTC(按 9 月 11 日约 77,000 美元的价格计算,约合 77 万美元)[5]。决定从一笔比特币存款中铸造多少合成比特币的链下代码,从存款人可以控制的交易部分中读取存款人的身份,使攻击者得以伪装成管理员,并将最低手续费推低至负值;随后该代码在未检查其符号的情况下,从存款金额中减去这一手续费,导致本应是减法的操作实际上变成了加法。
背景
Symbiosis 是一种跨链流动性协议,它通过锁定和铸造的方式而非直接转移资产本身来在链间转移价值:在支持智能合约的链上,用户的代币会被源链上的 Portal 合约锁定,而 Synthesis 合约会在目标链上铸造等量的合成代币(sToken);销毁该 sToken 则会释放原始资产 [6]。比特币通过这一路由以 syBTC 的形式流转,这是一种小数位数为 8(与比特币自身最小单位相匹配)的代币,发行于 BNB Smart Chain、以太坊和 Rootstock 上,并在资金池中与 BTCB、cbBTC、WBTC 和 RBTC 配对 [5]。

比特币不具备这些机制:没有智能合约,因此也就没有 Portal 合约来锁定存款。取而代之的是 portal(门户)——一个承担相同角色的指定比特币地址。存款是一笔支付给该地址的普通比特币交易,目标链所需的指令作为数据附加在交易中。该跨链桥所属的链下代码会读取这些数据,以获知谁在进行存款、金额是多少、以及合成代币应发送至何处,并执行该 portal 的手续费规则——该手续费在铸造金额被确定之前,从存款金额中扣除——其最低值是由管理员设置的一个参数。
比特币一侧发生的事情对目标链来说是不可见的,因此需要一个中继网络(Relayers Network)将由此产生的请求传递过去。该请求会以编码调用的形式到达 BridgeV2(该桥的链上入口点),再由其转发给 Synthesis。授权则依赖于一个 MPC 密钥:该协议的签名者们持有单个私钥的份额,并共同对请求生成一个签名。中继请求的入口点仅带有一个修饰符(modifier),且在调用被转发之前不做任何其他事情:

该修饰符会对请求进行哈希运算,并要求随附的签名对该 MPC 地址而言是有效的:

该合约所确认的仅仅是:该请求确实由 MPC 密钥签署。而该请求所携带的金额,则是在比特币一侧计算得出的。
漏洞分析
该缺陷并不在目标链的合约中,而在于读取比特币存款并将其转化为跨链请求的链下服务。其源代码并未公开,因此以下描述基于该团队的事后分析报告 [5],并结合了一份 2024 年的第三方审计报告,该报告中包含一段看似与此相关的代码片段 [7]。
存款流程中的两个缺陷必须同时凑到一起才能被利用,事后分析报告指出,二者单独存在时都不足以构成风险。
第一个缺陷在于存款人身份的识别方式。附加在存款交易上的指令会被解码以还原出存款人的身份,而解码器从交易数据中一个花费该输入的任何人都能控制的部分读取该身份。因此,存款人可以将自己冒充为协议所认可的任何一方,包括 portal 的管理员——这一角色负责设置该 portal 所能接受的最低手续费。
第二个缺陷在于该手续费的应用方式。2024 年的审计报告指出了 decodeWrap() 中通过从存款输出金额中减去手续费来计算铸造金额的这一行代码:
Value: types.Satoshi(tx.TxOut[idx].Value) - info.PortalFee,
其发现是:types.Satoshi 是一种有符号整数类型,而携带 PortalFee 的 info 结构体是不受信任且可由用户控制的,因此计算结果可以被驱动为负值,而当其作为无符号值被序列化传递给目标链时,会变成一个足够大的数值,从而可以铸造任意数量的合成比特币。该审计报告当时将此问题记录为已修复 [7]。而 2026 年的事后分析报告描述了同类型的故障:手续费在未经符号检查的情况下从存款中被扣除 [5],因此一个负的手续费不但没有减少存款金额,反而将其扩大了。存款流程中似乎没有任何机制能够将铸造金额限制在实际收到的比特币数量之内。
攻击分析
在大约四分钟内,共有十二笔伪造的铸造交易在 BNB Smart Chain、以太坊和 Rootstock 上得逞 [5]。以下分析基于其中一笔交易,即 BNB Smart Chain 上的交易 0x9a2bc0...21b9b959。

-
第一步:攻击者冒充 portal 的管理员,将该 portal 所能接受的最低手续费降至负值,从而使得一笔声明为负手续费的存款不会被拒绝,反而会被正常处理 [5]。
-
第二步:攻击者存入了 330 聪(satoshi),显示为
0x000000000000014a。到达 BNB Smart Chain 的请求携带的数值为0x400000000000014a——即相同的值,但设置了2^62对应的比特位,即4,611,686,018,427,388,234个最小单位。两者之差恰好为2^62,因此这笔存款所扣除的手续费为-4,611,686,018,427,387,904聪:330 - (-4,611,686,018,427,387,904) = 4,611,686,018,427,388,234该请求以这种形式被签署并中继传递。
-
第三步:
receiveRequestV2Signed()验证了该请求上的 MPC 签名,随后将其传递下去,最终到达metaMintSyntheticTokenBTC()。该函数根据源链 ID 解析出真实代币对应的合成代币表示形式,即syBTC,并以完整的已签署金额调用了synthesize()。铸造出的全部数量都被转入了攻击者的地址0x025122...5d3Ba2:46,116,860,184.27388234 syBTC。 -
第四步:攻击者将该合成代币卖入了持有它的资金池中。通过一个 Uniswap v4 的
syBTC/WBTC资金池进行的一次单独兑换,结算了18,446,744,072,845,450,682个最小单位的syBTC(约为上述交易铸造数量的四倍),并换出了4.38897292 WBTC。

经过进一步的路由转移,这笔兑换的收益最终折合约为 137 ether。
该团队暂停了比特币路由,约 15.2 BTC 的 portal 资金在数小时内被转移到了储备地址 [5]。团队提出了一项赏金计划,愿意向归还资金者支付所归还金额的 20% 作为奖励,该计划截止至 9 月 13 日 [8]。该协议的其他路由未受影响。
结论
没有密钥被盗,也没有任何合约被诱导执行其未曾编写用于执行的代码。目标链所验证的签名是真实有效的;而它所授权的那个数字,其实早已被链下代码以两种独立的方式错误地生成出来:一次是在其从存款人可控制的字段中读取存款人身份时,使攻击者得以冒充 portal 的管理员并将其最低手续费降至负值;另一次则是在其从存款中扣除该手续费时未检查其符号,导致一个负的手续费非但没有减少存款金额,反而将其扩大了。
从不受信任的输入中读取出的身份根本算不上是身份:一个特权角色只能通过存款人无法自行选择的信息来确立,例如实际授权了所花费输入的地址,并与协议所维护的名单进行核对——而不是一个存款人可以随意填写的字段。一个被减去的数值需要在两端都设置边界:手续费应当被要求介于零和存款金额之间,而铸造金额则永远不应被允许超过源链实际收到的数量。这两个约束条件中的任何一个,单独就足以阻止这次事件的发生。
参考资料
[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
关于 BlockSec
BlockSec 是一家全栈区块链安全与加密合规服务提供商。我们打造的产品与服务能够帮助客户开展代码审计(包括智能合约、区块链及钱包)、实时拦截攻击、分析安全事件、追踪非法资金,并满足反洗钱/反恐怖融资(AML/CFT)合规义务,覆盖协议与平台的全生命周期。
BlockSec 已在多个知名会议上发表区块链安全相关论文,报告过多起 DeFi 应用的零日攻击,成功拦截多起黑客攻击并挽回超过 2000 万美元的损失,同时为数十亿美元的加密资产提供了安全保障。
-
官方 Twitter 账号:https://twitter.com/BlockSecTeam



