#10 Panoptic Incident: XOR Linearity Breaks the Position Fingerprint Scheme

#10 Panoptic Incident: XOR Linearity Breaks the Position Fingerprint Scheme

On August 25, 2025, with the assistance of Cantina and Seal911, Panoptic conducted a white hat rescue operation, securing approximately $400K in at-risk funds [1]. The root cause was a flaw in the construction of s_positionsHash: the protocol used XOR to aggregate Keccak256 hashes of position IDs into a single fingerprint. While individual Keccak256 hashes remain collision-resistant, XOR's mathematical linearity makes the composite fingerprint insecure. An attacker can generate a set of forged position IDs whose XOR-aggregated hash matches any target fingerprint, bypassing the protocol's position verification and withdrawing collateral without repaying debt.

Background

Panoptic is a decentralized perpetual options trading protocol built on Ethereum that enables users to trade put and call options.

The function withdraw() has an input parameter positionList, which consists of a set of tokenIds. Each tokenId represents a position. The function withdraw() checks the debt status of each position based on s_positionsHash and then retrieves the user's collateral.

To save gas, the protocol does not store every position (i.e., tokenId) of the user. Instead, it calculates a fingerprint based on all the user's tokenIds and records it in s_positionsHash. The most significant 8 bits of each s_positionsHash represent numLegs, while the lower 248 bits represent the user position hash.

For each passed tokenId, the protocol calculates its hash, takes the lower 248 bits, and updates the user position hash by performing a bitwise XOR with the lower 248 bits of the current s_positionsHash.

Simultaneously, countLegs is adjusted based on the numerical range of the tokenId: it remains unchanged when tokenId is below 2642^{64}, and increases by 1, 2, or 3 when in the ranges (264,2112)(2^{64}, 2^{112}), (2112,2168)(2^{112}, 2^{168}), and (2168,2208)(2^{168}, 2^{208}) respectively. This is finally written to the top 8 bits of s_positionsHash to update numLegs.

Vulnerability Analysis

The root cause is a flaw in the contract's algorithm for constructing s_positionsHash, specifically the use of the XOR operation to aggregate Keccak256 hash results. While a single hash function remains secure, the mathematical linearity of the XOR operation renders the overall fingerprint algorithm (i.e., the XOR sum of hashes) insecure [2].

Linearity implies that an attacker does not need to break the hash function itself (i.e., reversing the tokenId from the hash). Instead, they can employ a combinatorial strategy: generating a large number of random tokenIds, calculating their Keccak256(tokenId), and from these hash values, selecting a specific subset such that the XOR sum of these tokenId hashes exactly matches the victim's target fingerprint.

This allows an attacker to pass a set of forged tokenIds when calling withdraw() to pass the health check and retrieve all collateral corresponding to s_positionsHash.

Theory

Assume the user's position fingerprint s_positionsHash has a user position hash of TT and numLegs of kk, with user tokenIds {t1,t2,,tn}\{t_1, t_2, \dots, t_n\}. Thus:

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

Here, each 248-bit hash value can be viewed as a 248-dimensional vector.

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

Therefore, TT resides in a 248-dimensional space composed of 0s and 1s (F2248\mathbb{F}_2^{248}). In this space, the XOR operation is equivalent to vector addition (Appendix I).

The attacker's goal is to find nn 248-dimensional vectors {v1,v2,,vn}\{v_1, v_2, \dots, v_n\} from all of the available vectors such that the lower 248 bits of their XOR sum equal TT. Thus, we can formulate the attacker's objective as a system of linear equations:

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

Specifically, we do not need to try to construct TT directly. Instead, we select nn linearly independent hash vectors {v1,v2,,vn}\{v_1, v_2, \dots, v_n\} (where nn equals the dimension 248) and use them as column vectors to construct an n×nn \times n matrix AA:

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

The problem then transforms into finding a coefficient vector xx such that:

Ax=TA \cdot x = T

Where x=[x1,x2,,xn]Tx = [x_1, x_2, \dots, x_n]^T, and xi{0,1}x_i \in \{0, 1\}.

According to linear algebra theory, as long as matrix AA is full rank, it spans the entire nn-dimensional space. This means that for any target TT, the system of equations has a unique solution. After solving for xx, we simply retain those vectors viv_i for which xi=1x_i=1; their XOR sum is TT.

Example

To facilitate understanding, we demonstrate this construction process with a case study. Assume the vectors are 3-dimensional, with each dimension consisting only of 0 or 1, and the target hash value is 101, i.e., T=[1,0,1]TT = [1, 0, 1]^T.

Next, we randomly generate 3 hash values:

  • 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

We construct matrix AA using these three vectors as columns and set up the equation 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}

Solving via Gaussian elimination, we obtain x=[1,0,1]Tx = [1, 0, 1]^T. This means we need to select v1v_1 and v3v_3 (corresponding to x1=1,x3=1x_1=1, x_3=1), and their XOR sum exactly equals the target TT.

Selecting n Linearly Independent Vectors

As previously mentioned, to solve Ax=TAx=T, we need to construct a full-rank matrix AA. Given that we are dealing with an extremely large vector space (m=2248m = 2^{248}), the core question is: How do we quickly select nn linearly independent vectors from this vast space?

Consider the simplest method: randomly generating nn tokenIds and selecting their hash results as vectors.

Over F2\mathbb{F}_2, the probability PP that nn randomly selected nn-dimensional vectors form a full-rank matrix is:

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

When nn is large (in this example, n=248n=248), this probability converges to a constant (Appendix II):

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

This means each random attempt has approximately a 28.9% probability of success. On average, an attacker only needs about 3.5 attempts to find a set of linearly independent vectors. Therefore, the computational cost is extremely low, and the attacker can quickly construct a matrix AA that satisfies the conditions.

To control the countLegs in the top 8 bits, we only need to adjust the range of the randomly generated tokenId values based on the target countLegs.

For example, if we want the numLegs in the forged fingerprint to be 0, we simply ensure that the nn randomly generated tokenIds are all less than 2642^{64}. Since the leg increment for tokenIds in this range is 0, regardless of which vectors the Gaussian elimination solution xx selects, the final accumulated numLegs will inevitably be 0.

Attack Analysis

The white hat rescuer initiated multiple rescue transactions. For simplicity, the following discussion is based on just one of these transactions [3].

The core logic consists of the following 5 steps:

  1. Borrow 0.23e8 WBTC and 28e18 WETH via flash loan from Aave.
  2. Deposit 0.23e8 WBTC and 28e18 WETH into the poWBTC contract and the 0x1f8d_poWETH contract.
  3. Call mintOptions() of the PanopticPool contract with a normal positionIdList to borrow funds and open a leveraged position.
  4. Call withdraw(), passing the forged tokenIds. Because these forged positions have a positionSize of 0, the function returns tokenRequired as 0, meaning the total collateral required for all positions is erroneously calculated as zero. Meanwhile, the s_positionsHash generated by these tokenIds is exactly the same as the one generated in step 3, allowing the rescuer to retrieve all collateral without repaying any debt.
  5. Repay the flash loan and execute the next round.

Summary

This incident highlights two compounding flaws that together enabled unauthorized collateral withdrawal.

  • XOR is not a secure aggregation function for hashes. Keccak256 is collision-resistant, but XOR's linearity means the XOR sum of multiple hashes does not inherit that property. Constructing a set of inputs whose hashes XOR to any target value reduces to solving a system of linear equations over F2\mathbb{F}_2, which is computationally trivial. Hash composition requires operations that preserve collision resistance, such as concatenation followed by re-hashing.
  • Missing validation of position IDs. The protocol did not verify whether passed positionIds corresponded to valid option positions. Values below 2642^{64} carry a countLegs of 0 and a positionSize of 0, meaning the forged positions incur no debt. This allowed the attacker to pass the health check with zero collateral requirement while matching the target fingerprint.

Reference

  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

Appendix

The following two appendices provide a mathematical explanation and proof of the statements in the main text, namely that the XOR operation is equivalent to vector addition (Appendix I) and the probability that a random matrix is full rank (Appendix II).

Appendix I

In the finite field F2={0,1}\mathbb{F}_2 = \{0, 1\}, addition is defined as addition modulo 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}

Observation shows this is identical to the Exclusive OR (XOR, symbol \oplus) logic operation.

For the nn-dimensional vector space F2n\mathbb{F}_2^n (in this case n=248n=248), the addition of two vectors u=[u1,,un]Tu = [u_1, \dots, u_n]^T and v=[v1,,vn]Tv = [v_1, \dots, v_n]^T is defined as component-wise modulo 2 addition:

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

Therefore, in the F2n\mathbb{F}_2^{n} vector space, vector addition is equivalent to bitwise XOR operation on vector components.

Appendix II

To make the matrix full rank, these nn vectors must be linearly independent.

The first vector v1v_1 can be any vector in F2n\mathbb{F}_2^n except the zero vector, providing 2n12^n - 1 choices; the second vector v2v_2 must not reside in the subspace spanned by {v1}\{v_1\}, which contains 212^1 vectors, leaving 2n22^n - 2 choices; following this logic, the kk-th vector vkv_k cannot be in the subspace spanned by the previous k1k-1 vectors, resulting in 2n2k12^n - 2^{k-1} possible choices.

The total number of such matrices (i.e., the order of GL(n,F2)GL(n, \mathbb{F}_2)) is:

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})

And the total number of all possible n×nn \times n matrices is (2n)n=2n2(2^n)^n = 2^{n^2}.

Thus, the probability PP is:

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})

Letting j=nkj = n - k, we can rewrite this expression as:

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

As nn \to \infty, this product converges to a constant:

P0.288788P \approx 0.288788

This means that for large nn, the probability that a random matrix is full rank is approximately 28.9%.


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.

Sign up for the latest updates
Top 10 "Awesome" Security Incidents in 2025

Top 10 "Awesome" Security Incidents in 2025

To help the community learn from what happened, BlockSec selected ten incidents that stood out most this year. These cases were chosen not only for the scale of loss, but also for the distinct techniques involved, the unexpected twists in execution, and the new or underexplored attack surfaces they revealed.

#9 1inch Incident: From Calldata Corruption to Forged Settlement: Binary Exploitation Goes On-Chain

#9 1inch Incident: From Calldata Corruption to Forged Settlement: Binary Exploitation Goes On-Chain

On March 5, 2025, a third-party resolver integrated with 1inch Fusion V1 was exploited for over $5M after an unsafe calldata reconstruction in the settlement flow allowed attacker-controlled interaction lengths to trigger a pointer underflow and inject forged settlement data. The impact was amplified by a broken trust boundary, where resolver contracts treated forwarded calldata as authoritative based only on msg.sender, letting attacker-crafted payloads inherit settlement-level privileges while still passing access control.

#7 Trust Wallet Incident: A Stolen API Key Turns the Official Update Channel into a Backdoor

#7 Trust Wallet Incident: A Stolen API Key Turns the Official Update Channel into a Backdoor

On December 25, 2025, Trust Wallet's Chrome extension (v2.68) was hit by a supply chain compromise that introduced a malicious backdoor, leading to the theft of about $8.5M in user funds. The injected code exfiltrated seed phrases to an attacker-controlled server, compromising wallets created or imported in that version, after which the attacker drained assets across multiple chains and laundered funds through non-KYC exchanges.