在過去一週(2026/09/07 - 2026/09/13),我們觀察到 2 起安全事件,估計總損失約為 3.2 億美元。
| 日期 | 事件 | 類型 | 估計損失 |
|---|---|---|---|
| 2026/09/06 * | Liquid Network | 缺陷的快取鍵構造 | ~$320M |
| 2026/09/11 | Symbiosis | 缺陷的鏈下存款驗證 | ~$770K |
*Liquid Network 事件發生於 9 月 6 日,未包含在上週的報告中。為求完整性,本次一併收錄。
Web3 最佳安全審計方
在上線前驗證設計、程式碼與業務邏輯
本週焦點: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 開頭的腳本輸出永遠無法被花費;它的存在是為了攜帶資料,而錨定退出正是以這種輸出方式表達的。
Liquid 預設也會隱藏金額。它不是將數字寫入輸出中,而是寫入對該數字的 Pedersen 承諾(commitment)。承諾具有可加性,因此節點可以確認一筆交易的輸入與輸出是否平衡,而無需得知其中任何一個具體數值。這項特性是雙面刃:承諾同樣可以隱藏一個表現為負數的值,這將使交易的輸出超過其輸入卻仍能保持平衡。因此,每個金額被隱藏的輸出都帶有一個範圍證明(rangeproof),用以證明所承諾的金額落在 [0, 2^64) 範圍內。驗證範圍證明的成本很高,且同一個輸出會被驗證不只一次——當交易進入記憶體池(mempool)時一次,當它被納入區塊時再一次——因此 Elements 會保留一份已接受證明的快取,以證明本身及其所驗證的資料作為索引鍵。
漏洞分析
該缺陷存在於 Elements(每個 Liquid 參與者所運行的節點軟體)如何快取已驗證過的範圍證明。
CachingRangeProofChecker::VerifyRangeProof() 為眼前的輸出推導出一個快取條目,一旦命中,便立即回傳成功,而不會再去檢查證明本身:

該條目來自 ComputeEntryRangeProof(),此函式將四個欄位依序寫入單一 SHA-256 串流中,並最終計算出摘要值:

這四個欄位分別是:範圍證明本身、對金額的 Pedersen 承諾、標識該輸出所持有資產的資產生成元(asset generator),以及 scriptPubKey——之所以納入後者,是為了讓一個被接受的證明綁定到某個特定的鎖定腳本上。
這四個欄位都沒有長度前綴或分隔符。範圍證明和 scriptPubKey 的長度是可變的;Pedersen 承諾和資產生成元則始終是 33 位元組的點。因此,摘要值僅記錄了串接後的位元組,沒有任何標記顯示一個欄位在何處結束、下一個欄位從何處開始。兩組不同的四個欄位若恰好串接成相同的位元組串流,便會產生相同的條目,而先被驗證的那一組會留下一個判定結果,供另一組收割。雜湊器的確使用了每個節點各自的隨機鹽值(salt)作為種子,但該鹽值同樣被添加在兩個串流的前綴,因此它只會改變摘要值,卻無法區分這兩個輸入。該缺陷已在提交記錄 94000967 中修復 [3]。
攻擊分析
以下分析基於交易 271147...187ec5 和 f24a4b...0a183f,兩者均被運行有漏洞程式碼的節點所接受。
- 步驟一:攻擊者發佈了一筆交易,其第一個輸出是攜帶 67 位元組資料的
OP_RETURN。
它的 scriptPubKey 讀作 6a 43 —— 即 OP_RETURN 操作碼後跟一個 67 位元組的推送(push)——接著是兩個 33 位元組的值,以及末尾的 6a。這推送的 67 個位元組並非這筆交易所需要的資料。它們是一個尚不存在的輸出的價值承諾與資產生成元。驗證此輸出時,會在其自身四個欄位所對應的條目下存入一個 valid 判定結果。
- 步驟二:攻擊者接著提交了通膨交易(inflation transaction)。其
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。
- 步驟三:攻擊者進行了錨定退出。兩筆交易透過正常的
OP_RETURN錨定退出輸出,分別銷毀了3,996.01834922和2.65138358L-BTC,總計3,998.66973280L-BTC。負責授權錨定退出的職能節點使用同樣存在漏洞的程式碼對其進行了驗證並釋放了比特幣:攻擊者在比特幣鏈上收到了3,995.99999857BTC 和2.49749857BTC,共計3,998.49749714BTC。
在事件處理過程中,網路暫停運行並分裂為兩條鏈 [4],隨後在拒絕該無效錨定退出的情況下恢復運作 [2]:如今某個區塊瀏覽器顯示原始交易(primer transaction)已確認,但通膨交易並未確認,儘管其所釋放的比特幣早已離開聯盟的錢包。次日 UTC 時間 16:09:25,攻擊者向聯盟的錨定錢包歸還了 3,400.00000000 BTC,剩餘約 598.5 BTC 仍未歸還 [2],雙方透過嵌入在比特幣交易中的訊息進行協商 [4]。
結論
攻擊者並未破解任何密碼學機制,也未曾竊取任何金鑰。此缺陷出在範圍證明快取如何識別其已經檢查過的內容。其索引鍵將四個欄位串接進單一雜湊串流中——其中兩個欄位長度可變——卻沒有任何標記顯示每個欄位的結束位置,因此第二組欄位得以重新切分相同的位元組,並命中相同的條目。一個已存入的 valid 判定結果因此被回傳給一個從未有任何節點驗證過的證明,而一旦範圍證明不再對其背後的金額加以約束,機密帳本便失去了唯一能維持其隱藏數值非負的保障。一個精心構造的輸出,變成了 4,000 個 L-BTC,而雙向錨定機制則將它們轉換成了主鏈上的比特幣。
任何由多個可變長度輸入所推導出的識別碼,都必須確定它們之間的邊界,無論是透過為每個欄位加上長度前綴,還是將各欄位分別雜湊進獨立的固定大小槽位中。若不這樣做,這種推導方式就不具備單射性(injective),兩個截然不同的輸入就可能被誤判為同一個。當快取被用來取代檢查時,這項要求尤為關鍵,因為在這種情況下,命中快取即等同於決定不執行該項檢查。
本週更多事件
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、Ethereum 及 Rootstock,並在資金池中與 BTCB、cbBTC、WBTC 及 RBTC 配對 [5]。

比特幣則完全不具備這些機制:沒有智能合約,因此也就沒有 Portal 合約來鎖定存款。取而代之的是入口(portal)——一個扮演相同角色的指定比特幣地址。存款是一筆支付給該地址的普通比特幣交易,並將發送至目標鏈的相關指令作為資料附加其中。橋接方所屬的鏈下程式碼會讀取這些資料,以了解是誰在存款、金額多少,以及合成代幣應發送至何處,並執行入口的手續費規則——該費用會在確定鑄造金額之前先從存款金額中扣除——其最低值是由管理員設定的一個參數。
比特幣端的任何資訊對目標鏈都是不可見的,因此需要一個中繼者網路(Relayers Network)來將產生的請求傳遞過去。該請求會以編碼呼叫的形式,抵達橋接方的鏈上入口點 BridgeV2,再由其轉發給 Synthesis。授權則依賴於一個 MPC 金鑰:協議的簽署方各自持有單一私鑰的一部分,並共同對該請求產生一個簽章。用於中繼請求的入口點僅帶有單一修飾符(modifier),在派發呼叫之前不做任何其他操作:

該修飾符會對請求進行雜湊處理,並要求隨附的簽章對 MPC 地址而言是有效的:

該合約所確認的僅僅是 MPC 金鑰對該請求進行了簽署。而請求中攜帶的金額則是在比特幣端計算得出的。
漏洞分析
此缺陷並不在目標鏈的合約中,而是在讀取比特幣存款並將其轉換為跨鏈請求的鏈下服務中。其原始碼並未公開,因此以下說明依據該團隊的事後報告 [5],並參考一份 2024 年第三方審計報告中似乎與此相關的一段程式碼片段 [7]。
存款路徑中的兩處缺陷必須同時發生才能造成問題,事後報告指出,任何一處單獨存在都不足以導致此次事件。
第一處缺陷在於存款人身份的識別方式。附加在存款交易上的指令會被解碼以還原出存款人身份,而該解碼器所取得的身份資訊,是來自交易資料中花費該輸入之人可自行控制的部分。因此,任何存款人都可以將自己冒充為協議所認可的任何一方,包括負責設定入口可接受最低手續費的管理員角色。
第二處缺陷在於該手續費的應用方式。2024 年的審計報告指出了 decodeWrap() 函式中透過從存款輸出金額中扣除手續費來計算鑄造金額的那一行程式碼:
Value: types.Satoshi(tx.TxOut[idx].Value) - info.PortalFee,
該報告的發現是:types.Satoshi 是一種帶符號的整數類型,而攜帶 PortalFee 的 info 結構體是不受信任且由使用者控制的,因此該計算結果可能被驅動為負值,並在被序列化為目標鏈上的無符號值時,變成一個足夠大的數字,從而可以鑄造任意數量的合成比特幣。當時審計報告記錄該問題已被修復 [7]。而 2026 年的事後報告則描述了同類型的失效:手續費在未經正負號檢查的情況下被從存款金額中扣除 [5],因此一個低於零的手續費反而增大了存款金額,而非減少它。存款路徑中似乎沒有任何機制將鑄造金額限制在實際收到的比特幣數量以內。
攻擊分析
在大約四分鐘內,共有十二筆偽造鑄造交易在 BNB Smart Chain、Ethereum 及 Rootstock 上通過 [5]。以下分析基於其中一筆交易,即 BNB Smart Chain 上的交易 0x9a2bc0...21b9b959。

-
步驟一:攻擊者冒充入口的管理員,將入口可接受的最低手續費壓低至負值,使得一筆聲稱手續費為負值的存款得以被處理,而非被拒絕 [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 的入口資金轉移至儲備地址 [5]。團隊提出將歸還金額的 20% 作為賞金,該提議開放至 9 月 13 日 [8]。協議的其他路由未受影響。
結論
沒有任何金鑰被竊取,也沒有任何合約被誘騙去執行其原本並非設計要執行的程式碼。目標鏈所驗證的簽章確實是真實有效的;但它所授權的那個數字,其實早已被鏈下程式碼以兩種不同的方式錯誤地生成:一次是在讀取存款人身份時,從存款人可自行控制的欄位中取值,讓攻擊者得以冒充入口的管理員,並將其最低手續費壓低至負值;另一次則是在未檢查正負號的情況下,將該手續費從存款金額中扣除,導致一個負值手續費反而增大了存款金額,而非減少它。
從不受信任的輸入中讀取出的身份,並非真正的身份:一個具有特權的角色,只能透過存款人無法自行選擇的資訊來確立,例如真正授權了被花費輸入的地址,並將其與協議所維護的名單進行核對——而不是一個存款人可自由填寫的欄位。任何被扣減的數值都需要在兩端設立邊界:手續費應被要求介於零與存款金額之間,而鑄造金額則永遠不應被允許超過源鏈實際收到的數量。這兩項邊界檢查中,任何一項單獨存在都足以阻止此次事件的發生。
參考資料
[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 應用程式的零日攻擊,成功攔截多起駭客攻擊,挽回超過 2,000 萬美元的損失,並保障了數十億美元加密資產的安全。
-
官方 Twitter 帳號:https://twitter.com/BlockSecTeam



