Vulnerability Post Mortem
Summary
On 06/05/26 the internal zkBob security team discovered a critical double-spend vulnerability in one of the ZK circuits used by the protocol.
There is no public evidence that the vulnerability was ever exploited. However, we cannot rule out the possibility entirely.
As a result, all zkBob pools are being deprecated and placed into withdraw-only mode. If you hold funds in any active zkBob account, go to https://app.zkbob.com/ and withdraw them now.
Impact
All live zkBob pools were affected and required immediate action:
Polygon PoS USDC pool
Optimism USDC pool
Optimism ETH pool
Timeline
2026/06/05 β Potential circuit logic flaw identified during a routine security review
2026/06/05 β All zkBob pools paused as a precaution, pending further investigation
2026/06/06 β Vulnerability confirmed as critical
2026/06/07 β Fix planning started
2026/06/11 β First public statement acknowledging the vulnerability released
2026/06/15 β Circuit fix applied; new version of the protocol circuits released
2026/06/18 β Updated UI/SDK packages released
2026/06/22 β Updated circuit parameters released following an internal Groth16 ceremony
2026/06/23 β Updated smart contract supporting withdraw-only mode released and deployed
2026/07/02 β All updated components (libraries, SDK, UI, smart contracts) passed internal review
2026/07/03 β Smart contract upgrade scheduled through the governance multisig via a timelock
2026/07/05 β Smart contract upgrade executed
2026/07/06 β Updated UI released; zkBob pools restarted in withdraw-only mode; public restart announcement released
Root cause
zkBob uses a hybrid model that combines UTXO and account-balance accounting. One of its ZK circuits is designed to prove that only valid account state transitions are accepted, ensuring that every inbound UTXO note can be spent only once and that the sum of all consumed UTXO notes matches the change in account balance during a state update.
For existing accounts, the circuit worked as intended. However, an edge-case vulnerability was identified for accounts being created for the first time while already holding unspent incoming UTXO notes.
Because such an account is being created for the first time, some state-transition rules were relaxed. In particular, since no prior account snapshot existed in the global zkBob Merkle tree, the circuit did not validate the Merkle proof attesting to the correctness of the prior account state. An empty account with zero values was used as a stub, and the zkBob SDK filled the Merkle proof with zeros. This Merkle proof data is also used as an input when computing input_pos_index and nullifier, where the nullifier ensures that all older account states are invalidated during each state transition.
However, because the circuit would accept any arbitrary data as the Merkle proof for a just-initialized account, an attacker could control input_pos_index and nullifier by crafting malicious Merkle proof data. As a result, an attacker could initialize multiple accounts that double-spend the same inbound UTXO notes: each circuit run would produce a different nullifier and therefore fail to correctly invalidate the previously used zero-account states.
Vulnerable circuit: https://github.com/zkBob/libzeropool-zkbob/blob/903369517b3f8133cd398787478ba5bf46cca2c2/src/circuit/tx.rs#L187-L195
Fix
An additional constraint was introduced to enforce that only one valid nullifier and account state can exist for a not-yet-initialized account, preventing any new double-spend transactions in the future.
Due to the nature of the zkBob protocol, it is technically impossible to reliably prove whether the bug was ever exploited on any of the publicly deployed pools. This is a common limitation of ZK-based privacy solutions. We have no reason to believe it was exploited: no public evidence or indication was found suggesting otherwise, and no suspicious token balances or withdrawal activity were identified during our internal retroactive review.
Nevertheless, we cannot rule out the possibility entirely. We have therefore decided to permanently deprecate the existing zkBob pools, disable all deposits, and place the pools into withdraw-only mode. As a precaution, all withdrawals will pass through a manually reviewed queue; we expect all submitted withdrawals to be processed smoothly and redeemed at a 1:1 rate on a regular schedule.
User action
If you hold any remaining funds in a zkBob account in any pool, go to https://app.zkbob.com/ and start a withdrawal submission.
Please note that submitted withdrawals will not be processed immediately. All withdrawal claims are recorded on-chain and processed manually within 1β2 weeks, after which funds are automatically credited to your wallet address. Withdrawals are processed in FIFO order in batches, unless suspicious activity or a potential solvency issue is identified and requires further action from the team.
Deposits and transfers in the existing pools will remain disabled.
Last updated
Was this helpful?