Back to Blog

第10號全景事件:XOR線性破壞位置指紋方案

Code Auditing
February 13, 2026
7 min read

2025 年 8 月 25 日,在 Cantina 和 Seal911 的協助下,Panoptic 進行了白帽救援行動,保護了約 40 萬美元的風險資金 [1]。根本原因是 s_positionsHash 建構方式存在缺陷:協議使用異或(XOR)運算將倉位 ID 的 Keccak256 哈希值聚合為單一指紋。雖然單個 Keccak256 哈希值仍具備抗碰撞性,但 XOR 的數學線性使得複合指紋變得不安全。攻擊者可以產生一組偽造的倉位 ID,使其 XOR 聚合後的哈希值與任何目標指紋相匹配,從而繞過協議的倉位驗證並在不償還債務的情況下提取抵押品。

背景

Panoptic 是一個建立在以太坊上的去中心化永續期權交易協議,允許用戶交易看跌和看漲期權。

withdraw() 函數有一個名為 positionList 的輸入參數,由一組 tokenId 組成。每個 tokenId 代表一個倉位。withdraw() 函數會根據 s_positionsHash 檢查每個倉位的債務狀態,然後提取用戶的抵押品。

為了節省 Gas,協議不會儲存用戶的每一個倉位(即 tokenId)。相反,它根據用戶的所有 tokenId 計算出一個指紋,並將其記錄在 s_positionsHash 中。每個 s_positionsHash 的高 8 位代表 numLegs,而低 248 位代表 user position hash

對於每個傳入的 tokenId,協議會計算其哈希值,取低 248 位,並透過將其與當前 s_positionsHash 的低 248 位進行按位異或運算來更新 user position hash

同時,countLegs 會根據 tokenId 的數值範圍進行調整:當 tokenId 小於 2642^{64} 時保持不變;當處於 (264,2112)(2^{64}, 2^{112})(2112,2168)(2^{112}, 2^{168})(2168,2208)(2^{168}, 2^{208}) 範圍內時,分別增加 1、2 或 3。最終將此結果寫入 s_positionsHash 的高 8 位以更新 numLegs

漏洞分析

根本原因在於合約建構 s_positionsHash 的演算法存在缺陷,具體表現為使用 XOR 運算來聚合 Keccak256 哈希結果。雖然單個哈希函數仍然安全,但 XOR 運算的數學線性使得整體的指紋演算法(即哈希值的 XOR 和)變得不安全 [2]。

線性意味著攻擊者不需要破解哈希函數本身(即從哈希中反推 tokenId)。相反,他們可以採用組合策略:產生大量隨機的 tokenId,計算其 Keccak256(tokenId),並從這些哈希值中選擇一個特定的子集,使得這些 tokenId 哈希值的 XOR 和精確匹配受害者的目標指紋。

這使得攻擊者可以在呼叫 withdraw() 時傳入一組偽造的 tokenId,從而通過健康檢查並提取 s_positionsHash 對應的所有抵押品。

理論

假設用戶的倉位指紋 s_positionsHashuser position hashTTnumLegskk,用戶的 tokenId{t1,t2,,tn}\{t_1, t_2, \dots, t_n\}。因此:

i=1k[Hash(ti)(mod2248)]=T\bigoplus_{i=1}^{k} [\text{Hash}(t_i) \pmod{2^{248}}] = T

這裡,每個 248 位的哈希值可以被視為一個 248 維的向量。

Hash(ti)(mod2248)=[b0,b1,,b247]T,其中 bi{0,1}\text{Hash}(t_i) \pmod{2^{248}} = [b_0, b_1, \dots, b_{247}]^T, 其中\ b_i \in \{0, 1\}

因此,TT 存在於由 0 和 1 組成的 248 維空間(F2248\mathbb{F}_2^{248})中。在這個空間中,XOR 運算等同於向量加法(附錄 I)。

攻擊者的目標是從所有可用向量中找出 nn 個 248 維向量 {v1,v2,,vn}\{v_1, v_2, \dots, v_n\},使得它們的 XOR 和的低 248 位等於 TT。因此,我們可以將攻擊者的目標表述為一個線性方程組:

x1v1+x2v2++xnvn=Tx_1 v_1 + x_2 v_2 + \dots + x_n v_n = T

具體來說,我們不需要嘗試直接建構 TT。相反,我們選擇 nn 個線性無關的哈希向量 {v1,v2,,vn}\{v_1, v_2, \dots, v_n\}(其中 nn 等於維度 248),並將它們作為列向量來建構一個 n×nn \times n 矩陣 AA

A=[v1,v2,,vn]A = [v_1, v_2, \dots, v_n]

問題隨即轉換為尋找係數向量 xx,使得:

Ax=TA \cdot x = T

其中 x=[x1,x2,,xn]Tx = [x_1, x_2, \dots, x_n]^T,且 xi{0,1}x_i \in \{0, 1\}

根據線性代數理論,只要矩陣 AA 滿秩,它就生成了整個 nn 維空間。這意味著對於任何目標 TT,該方程組都有唯一解。解出 xx 後,我們只需保留那些 xi=1x_i=1 對應的向量 viv_i,它們的 XOR 和即為 TT

範例

為了便於理解,我們透過一個案例研究來演示此建構過程。假設向量是 3 維的,每個維度僅由 0 或 1 組成,且目標哈希值為 101,即 T=[1,0,1]TT = [1, 0, 1]^T

接下來,我們隨機產生 3 個哈希值:

  • v1=[1,1,0]Tv_1 = [1, 1, 0]^T
  • v2=[0,1,0]Tv_2 = [0, 1, 0]^T
  • v3=[0,1,1]Tv_3 = [0, 1, 1]^T

我們使用這三個向量作為列建構矩陣 AA,並建立方程 Ax=TAx=T

[100111001][x1x2x3]=[101]\begin{bmatrix} 1 & 0 & 0 \\ 1 & 1 & 1 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x_1 \\ x_2 \\ x_3 \end{bmatrix} = \begin{bmatrix} 1 \\ 0 \\ 1 \end{bmatrix}

透過高斯消去法求解,我們得到 x=[1,0,1]Tx = [1, 0, 1]^T。這意味著我們需要選擇 v1v_1v3v_3(對應於 x1=1,x3=1x_1=1, x_3=1),它們的 XOR 和正好等於目標 TT

選擇 n 個線性無關向量

如前所述,要解出 Ax=TAx=T,我們需要建構一個滿秩矩陣 AA。鑑於我們處理的是一個極大的向量空間(m=2248m = 2^{248}),核心問題是:如何從這個巨大的空間中快速選擇 nn 個線性無關的向量?

考慮最簡單的方法:隨機產生 nntokenId 並選擇它們的哈希結果作為向量。

F2\mathbb{F}_2 上,nn 個隨機選擇的 nn 維向量形成滿秩矩陣的機率 PP 為:

P(n)=k=0n1(12k2n)P(n) = \prod_{k=0}^{n-1} \left(1 - \frac{2^k}{2^n}\right)

nn 很大時(在本例中 n=248n=248),該機率收斂為一個常數(附錄 II):

limnP(n)0.28879\lim_{n \to \infty} P(n) \approx 0.28879

這意味著每次隨機嘗試都有大約 28.9% 的成功機率。平均而言,攻擊者只需要約 3.5 次嘗試即可找到一組線性無關向量。因此,計算成本極低,攻擊者可以快速建構滿足條件的矩陣 AA

為了控制高 8 位的 countLegs,我們只需要根據目標 countLegs 調整隨機產生的 tokenId 值的範圍即可。

例如,如果我們希望偽造指紋中的 numLegs 為 0,我們只需確保 nn 個隨機產生的 tokenId 全部小於 2642^{64}。由於此範圍內 tokenId 的 leg 增量為 0,無論高斯消去法的解 xx 選擇了哪些向量,最終累積的 numLegs 都必然為 0。

攻擊分析

白帽救援人員發起了多次救援交易。為簡潔起見,以下討論僅基於其中一筆交易 [3]。

核心邏輯包含以下 5 個步驟:

  1. 透過 Aave 閃電貸借入 0.23e8 WBTC 和 28e18 WETH。
  2. 將 0.23e8 WBTC 和 28e18 WETH 存入 poWBTC 合約和 0x1f8d_poWETH 合約。
  3. 使用正常的 positionIdList 呼叫 PanopticPool 合約的 mintOptions(),以借入資金並開立槓桿倉位。
  4. 呼叫 withdraw(),並傳入偽造的 tokenId。由於這些偽造倉位的 positionSize 為 0,該函數返回的 tokenRequired 為 0,這意味著所有倉位所需的總抵押品被錯誤地計算為零。同時,由這些 tokenId 產生的 s_positionsHash 與步驟 3 中產生的完全相同,從而允許救援人員在不償還任何債務的情況下提取所有抵押品。
  5. 償還閃電貸並執行下一輪。

總結

此次事件突顯了兩個共同作用的缺陷,它們共同實現了未經授權的抵押品提取。

  • XOR 對於哈希而言並非安全的聚合函數。 Keccak256 具有抗碰撞性,但 XOR 的線性意味著多個哈希值的 XOR 和並不繼承該屬性。建構一組其哈希值 XOR 為任何目標值的輸入,簡化為在 F2\mathbb{F}_2 上求解線性方程組,這在計算上是微不足道的。哈希複合需要能夠保持抗碰撞性的運算,例如連接後再進行哈希處理。
  • 缺少倉位 ID 的驗證。 協議未驗證傳入的 positionId 是否對應於有效的期權倉位。小於 2642^{64} 的值具有 0 的 countLegs 和 0 的 positionSize,這意味著偽造的倉位不會產生債務。這允許攻擊者在抵押品要求為零的情況下透過健康檢查,同時匹配目標指紋。

參考資料

  1. https://x.com/Panoptic_xyz/status/1961187739866644524

  2. https://cseweb.ucsd.edu/~mihir/papers/inchash.pdf

  3. https://app.blocksec.com/explorer/tx/eth/0x67a45dfe5ff4b190058674d7c791bbdc48e889f319f937c24fa13a5f9093f088

附錄

以下兩個附錄提供了對正文中陳述的數學解釋和證明,即 XOR 運算等同於向量加法(附錄 I)以及隨機矩陣滿秩的機率(附錄 II)。

附錄 I

在有限域 F2={0,1}\mathbb{F}_2 = \{0, 1\} 中,加法定義為模 2 加法:

0+0=00+1=11+0=11+1=0(mod2)\begin{aligned} 0 + 0 &= 0 \\ 0 + 1 &= 1 \\ 1 + 0 &= 1 \\ 1 + 1 &= 0 \pmod 2 \end{aligned}

觀察發現這與邏輯運算「異或」(XOR,符號為 \oplus)完全相同。

對於 nn 維向量空間 F2n\mathbb{F}_2^n(本例中 n=248n=248),兩個向量 u=[u1,,un]Tu = [u_1, \dots, u_n]^Tv=[v1,,vn]Tv = [v_1, \dots, v_n]^T 的加法定義為分量模 2 加法:

u+v=[u1+v1(mod2)un+vn(mod2)]=[u1v1unvn]=uvu + v = \begin{bmatrix} u_1 + v_1 \pmod 2 \\ \vdots \\ u_n + v_n \pmod 2 \end{bmatrix} = \begin{bmatrix} u_1 \oplus v_1 \\ \vdots \\ u_n \oplus v_n \end{bmatrix} = u \oplus v

因此,在 F2n\mathbb{F}_2^n 向量空間中,向量加法等同於向量分量上的按位異或運算。

附錄 II

要使矩陣滿秩,這 nn 個向量必須線性無關。

第一個向量 v1v_1 可以是 F2n\mathbb{F}_2^n 中除零向量外的任何向量,提供 2n12^n - 1 種選擇;第二個向量 v2v_2 不能位於 {v1}\{v_1\} 生成的子空間中(該子空間包含 212^1 個向量),因此有 2n22^n - 2 種選擇;根據此邏輯,第 kk 個向量 vkv_k 不能位於前 k1k-1 個向量生成的子空間中,導致有 2n2k12^n - 2^{k-1} 種可能的選擇。

此類矩陣的總數(即 GL(n,F2)GL(n, \mathbb{F}_2) 的階數)為:

N=k=0n1(2n2k)=(2n1)(2n2)(2n4)(2n2n1)N = \prod_{k=0}^{n-1} (2^n - 2^k) = (2^n - 1)(2^n - 2)(2^n - 4)\cdots(2^n - 2^{n-1})

而所有可能的 n×nn \times n 矩陣總數為 (2n)n=2n2(2^n)^n = 2^{n^2}

因此,機率 PP 為:

P=k=0n1(2n2k)2n2=k=0n1(2n2k2n)=k=0n1(12k2n)P = \frac{\prod_{k=0}^{n-1} (2^n - 2^k)}{2^{n^2}} = \prod_{k=0}^{n-1} \left( \frac{2^n - 2^k}{2^n} \right) = \prod_{k=0}^{n-1} (1 - \frac{2^k}{2^n})

j=nkj = n - k,我們可以將該表達式重寫為:

P=j=1n(112j)P = \prod_{j=1}^{n} \left(1 - \frac{1}{2^j}\right)

nn \to \infty 時,該乘積收斂為一個常數:

P0.288788P \approx 0.288788

這意味著對於較大的 nn,隨機矩陣滿秩的機率約為 28.9%


關於 BlockSec

BlockSec 是一家全端區塊鏈安全與加密合規服務供應商。我們建構產品與服務,協助客戶在協議與平台的全生命週期內進行代碼審計(包括智慧合約、區塊鏈與錢包)、即時攔截攻擊、分析安全事件、追蹤非法資金,並履行反洗錢/打擊資助恐怖主義(AML/CFT)義務。

BlockSec 已在知名會議上發表多篇區塊鏈安全論文,通報了多項 DeFi 應用的零日漏洞,阻斷了多次駭客攻擊並成功挽救超過 2000 萬美元資金,保護了數十億美元的加密貨幣資產。

Best Security Auditor for Web3

Validate design, code, and business logic before launch. Aligned with the highest industry security standards.

BlockSec Audit