CryptoReal
CASE FILE — Mar 6, 2026

The Unfinished Ceremony: How a Skipped Setup Step Broke Two ZK Protocols in a Week

For years, zero-knowledge proofs were pitched as a way to remove trust from the equation entirely: don't believe the team, verify the math. On that pitch, a decade of privacy tools, rollups, and identity systems got built. Then, within the same week, two separate protocols showed what happens when the math is never actually finished — just assumed to be.

The pattern was almost identical in both cases. Neither incident involved a clever attack or a freshly discovered class of vulnerability. Instead, a placeholder value that the toolchain expected developers to replace before launch was left untouched, sitting behind real user funds until someone tried it.

Veil Cash was hit first, losing 2.9 ETH in one transaction. Days later, FoomCash lost $2.26 million to the exact same underlying flaw. Together, these were the first confirmed instances of live, in-production ZK cryptography actually being broken by an attacker rather than merely theorized about.

The cryptography itself wasn't at fault — it had simply never been completed. And if trustless systems are supposed to let the proof stand on its own, what does it mean when the proof was empty from the day it went live?

Sources and credit for this reporting: CoinsBench, ZK Security, Tornado Cash, CoinDesk, Trail of Bits, 0xPARC, Veil.Cash, DK27ss, Pashov Audit Group, Defimon Alerts, Cryptopolitan, FoomCash, OtterSec, Expander, Solana, and Dedaub.

01A Familiar Class of Bug, Long Assumed Too Obscure to Exploit

This isn't a new category of weakness — what's new is someone finally bothering to use it. In 2019, Tornado Cash's team found a live bug in their own circomlib circuit: a single misplaced character (= where <== belonged) that would have let anyone forge a Merkle root and empty the contract. They exploited their own flaw first, published their findings, and closed the gap before anyone else could act on it.

In 2021, Zcash quietly fixed an infinite-mint bug buried in its trusted-setup parameters — a flaw that had existed for years, assumed safe because exploiting it, in the team's own words, would need "a high level of technical and cryptographic sophistication that very few people possess."

That assumption became the industry's unwritten rule: the underlying math holds, and the surrounding code is complicated enough that attackers won't bother digging through it. Complexity itself was treated as a defense.

By 2022, Trail of Bits had cataloged an entire family of these issues under the name "Frozen Heart" — Fiat-Shamir implementation errors where public inputs weren't locked into the hash transcript before challenges were generated. SnarkJS, Dusk Network, and ConsenSys' gnark were all implicated. The research was public, 0xPARC even maintained an open tracker documenting the patterns, and the ecosystem largely patched what it found and moved on. The working theory remained: ZK code is too dense for attackers to bother parsing at scale. Nobody seriously asked what would happen once someone finally did — and once a working proof-of-concept was posted publicly on GitHub for anyone to copy.

02Veil Cash: A Placeholder Value Nobody Replaced

Around February 20th, a small Base-based privacy pool called Veil Cash was drained to zero — not through a flash loan, oracle manipulation, or insider access, but via a single transaction that looped 29 times, extracting 0.1 ETH per pass until the pool was empty.

Veil Cash was a fork of Tornado Cash that ran permissioned deposits through a validator contract with zero-knowledge withdrawals — an otherwise unremarkable protocol on an otherwise busy chain.

The attacker submitted nullifier hashes ranging from 0xdead0000 through 0xdead001c — values invented out of thin air that the verifier accepted without complaint, not because it chose to, but because it had no way to reject them.

The root cause traces back to how Groth16 verification requires the gamma and delta values to be distinct, each generated independently during a trusted-setup ceremony. Skip that ceremony step, and both values default to the same thing: the BN254 G2 generator — the built-in Phase 2 placeholder in snarkjs that the toolchain expects developers to overwrite before going live. Nobody had run that command.

Notably, Pashov Audit Group had reviewed Veil Cash, but the verifier contract itself sat outside the scope of that engagement. Using the forged nullifier hashes, the attacker called withdraw() 29 times against the 0.1 ETH pool, extracting a total of 2.9 ETH without ever making a deposit, all inside a single transaction.

Drain transaction: 0x5ff6dbc33e77fab8dc086bb9ea3c88f1ba81df198d24ec9fc0c5b50fb1a4a17d

Shortly afterward, DefimonAlerts — the security monitoring arm of Decurity — stepped in and rescued 2.025 ETH from the remaining pools, returning it to Veil.Cash.

Whitehat rescue transaction: 0x42019ae0532a25e40068fa16d2c7e3a9ce006f0b872d84ae5b4540b368a0268c

Later, at approximately 22:05 UTC, the original attacker sent the drained funds back to Veil.Cash. Whether this reflected a change of heart, a whitehat demonstration, or something else is not something the transaction record clarifies.

Exploiter return transaction: 0x5a16ada9fbb068cc2492df57694b25ea56734eb6599c8f269767a23d10e0d162

Sums referenced in this case file

Veil.Cash later confirmed that all funds from the affected pools had been fully recovered. But the exploit technique itself was now public knowledge: DK27ss posted a full proof-of-concept on GitHub, and CoinsBench published a detailed technical breakdown of the mechanics. What had cost 2.9 ETH was really a demonstration — the open question was who else had shipped the same unfinished setup, and how much value was sitting behind it.

03FoomCash: The Same Flaw, at Larger Scale

FoomCash's case involved one team, one skipped setup step, and a verifier that had been broken since deployment. Rekt covered the incident in depth separately — including the bounty dispute, the standoff on the EthSecurity Telegram, and a sanitized post-mortem that framed the response as an "elite white-hat" effort even though on-chain records tell a different story. The underlying math didn't change depending on the narrative, even if the public framing did.

What made this notable beyond the individual incident was what followed. Two days after FoomCash's own March 1st post-exploit writeup, OtterSec released "Unfaithful Claims: Breaking 6 zkVMs," documenting a related but distinct problem.

04A Second, Broader Pattern: Unfaithful Claims Across Six zkVMs

Researchers Himanshu Sheoran and Valter Wik found the same category of Fiat-Shamir binding failure independently present across six separate zkVM implementations, disclosed over several months from late 2025 into early 2026: Jolt (built by a16z), Nexus, Cairo-M (Kakarot Labs), Ceno (Scroll), Expander (Polyhedra), and Binius64. Six different teams, six different codebases, six different proof systems — all landing on the same underlying mistake, distinct from the Groth16 misconfiguration behind FoomCash but following a comparable pattern: gaps in implementation that let a prover cheat the verification process.

The mechanism: if any value feeding a verification equation isn't hashed into the transcript before the corresponding challenge is derived, a prover can effectively know the challenge in advance and choose the value retroactively to satisfy the equation. The proof passes even though the underlying statement may be entirely false. Across all six systems, values controlled by the prover — opening claims, lookup sums, public inputs — were entering verification equations without first being absorbed into the transcript. In every instance, the fix amounted to one or two lines of code; spotting the bug in the first place required tracing the entire verification pipeline and asking what happens if the prover picks a value only after seeing the challenge.

To underline how serious this class of bug is, OtterSec issued a public challenge: submit a valid proof of a counterexample to Fermat's Last Theorem (integers a, b, c ≥ 1 satisfying a³+b³=c³). In a live blockchain context, the same underlying technique could be used to conjure funds that don't exist.

Most of the six vulnerable systems were patched between October and January 2026, following private disclosure. Ceno, Scroll's zkVM, had an issue filed on November 10th that remained open as of OtterSec's publication date. Expander's $500,000 bug bounty was still awaiting confirmation at that time.

This bug class isn't new either — Trail of Bits described the same Fiat-Shamir binding failure in 2022 as "Frozen Heart," affecting Iden3's SnarkJS, Dusk Network's plonk implementation, and ConsenSys' gnark. Solana quietly patched one occurrence in April 2025 and a second in June 2025, both resolved before any exploit occurred. Despite the existing research and public trackers, six teams building independently still arrived at the identical mistake.

OtterSec's explanation for why this keeps recurring is instructive: academic literature typically describes interactive protocols where a real verifier issues live random challenges, making the binding implicit and unremarked-upon. The papers don't spell out what belongs in the transcript once the protocol is made non-interactive — that responsibility falls entirely on whoever implements it. In modular zkVM designs, each architectural layer tends to assume a neighboring layer already handles the binding, and because every hash operation carries a real computational cost, optimization pressure quietly nudges values out of the transcript under the assumption that it's "probably fine."

Unit and integration tests exercise honest provers — nothing automated simulates an adversarial one. Catching this requires a person who understands the entire verification flow asking the one question most teams skip. At the time of disclosure, most of the six zkVMs held no meaningful live value, and no funds were actually lost — the flaws were caught and reported before anyone malicious found them, which matters. But zkVM-based rollups represent a likely future direction for the industry, where the proof system sits underneath the chain itself rather than in a single contract. Whether this class of bug scales safely once real value depends on that layer remains untested in practice, and the safety margin is shrinking.

Two distinct toolchains, two distinct failure modes — a skipped CLI step in one case, a missing transcript binding in the other. Not the same bug, but the same unanswered question in both cases: who actually verified what the verifier would accept?

05Why Deployment Rarely Gets the Same Scrutiny as Design

Structurally, the answer to that question is usually nobody — because in most audit engagements, nobody is asked to check. Circuit design gets audited; deployment configuration typically doesn't. Verification keys — the exact cryptographic surface that decides whether a proof gets accepted — are rarely handed over to auditors at all. This isn't negligence so much as an unspoken boundary both sides accept, often without either side fully considering what falls outside it.

Pashov Audit Group's review of Veil Cash explicitly excluded the misconfigured verifier from its engagement scope, something the firm flagged only after the fact. Scope is negotiable, and deployment parameters frequently never reach auditors in the first place — but the practical outcome was a protocol that passed its audit while running a verifier that would accept literally any submitted proof.

zkSecurity, which published the Groth16 post-mortem following the FoomCash drain, acknowledged the same exposure in their own audit history: their first reaction on reviewing the vulnerability was that it could easily have happened to a project they themselves had reviewed, since deployment configuration is rarely part of the audit scope — something they said they now intend to change.

Following the FoomCash incident, Dedaub ran an EVM-wide scan using its scanning tooling to find contracts storing the same element twice under two different names. No other live, significantly funded contracts turned up — suggesting the immediate blast radius on EVM chains was contained. A parallel GitHub search, however, turned up a different problem: several repositories still carry the identical δ=γ fingerprint. One flagged example was an educational Tornado Cash implementation with 248 stars — precisely the kind of repo developers fork as a starting template, deploy without reading the setup instructions, and trust simply because it's popular.

The Groth16 exploit's proof-of-concept is public, and so is the version that scaled it up. The vulnerable repositories remain online. The gap between those two facts is measured only in how many people haven't stumbled onto them yet.

In response, zkSecurity is adding Phase 2 detection to zkao, its continuous monitoring tool. Five of the six affected zkVMs were patched between October and December 2025, each within weeks of Sheoran and Wik's private disclosures — Ceno's issue remains open. The ecosystem is responding to what's already been surfaced, but that isn't the same as knowing what hasn't been found yet.

06The Underlying Question

Zero-knowledge systems were marketed as a way to remove the need to trust a team's competence — you simply verify the proof. But that guarantee only holds if the setup ceremony that generated the parameters was actually completed, if the transcript genuinely binds every prover-controlled value, and if someone actually agreed to check the parts that fall outside a typical audit's scope.

Within a single week, two ZK systems were exploited in production for the first time in the technology's history — not because the cryptography itself failed, but because the surrounding process that gives that cryptography meaning was either skipped entirely or never assigned to anyone in the first place.

Veil Cash demonstrated the concept. FoomCash proved it scales. The six affected zkVMs proved it isn't isolated to one team. Working proofs-of-concept are now public, the vulnerable repositories remain up, and Ceno's disclosure is still unresolved.

Every public disclosure doubles as a blueprint, and in this space blueprints don't stay unused for long. For a decade, the ZK ecosystem told users that the math itself was the guarantee. It turns out the math needed a completed ceremony, the ceremony needed someone to actually run the command, and nobody had clearly decided whose job that was.

Groth16Trusted SetupZK Security
Investigation alerts

Get new scam files the moment we publish them — usually 2–3 emails a week.

Enter a valid email address.

No spam, unsubscribe anytime. We never sell your data. Crypto assets are volatile and high-risk; nothing here is financial advice.

You're on the list. Watch your inbox for the next scam file.