Full Report
Zero Knowledge Proofs (ZKP) are a crazy but black-magic mechanism for knowing that something happened without revealing what happened. For instance, proving that a person voted without giving up that person. Usually, this requires interactions or entities sending data back and forth. By hashing everything, it's possible to make proofs non-interactive via the Fiat-Shamir heuristic. The system doesn't reexecute each instruction one by one. Instead, it uses algebraic constraints over the committed polynomials to calculate correctness. If an attacker can send executable code that was never executed or is an invalid state transition, this would violate the property of soundness. The verification flow is done as follows: Fix public statement data. Parse proof payload, such as commitments, reduction messages, and openings. Rebuild the Fiat-Shamir challenges from the transcript data. Check constraint equations and check PCS/opening consistency. Accept the state change if all checks are consistent. The Fiat-Shamir transformation is required because blockchains can't have real-time communication. Instead of generating a random number via a challenge protocol, the verifier hashes the existing state. This creates a really important invariant: the hash must include everything that affects verification BEFORE the challenges derived from it are used. If a value affects the equation but isn't used in the hash, it's possible to control that value in the rest of the protocol. The term absorbed is used a lot; it's similar to a hash function. The SumCheck protocol proves that a polynomial sums to a claimed value over a Boolean hypercube. By doing some clever math, it's possible to turn this from 2^n to a single evaluation for a global check called the claimed_sum. This value is provided and MUST be part of the committed value. This issue was found 6 times in various codebases. Finding values that meet the attack's needs requires advanced math that is either trivial to solve or requires some brute-forcing. Regardless, this missing binding happened in six separate implementations and was catastrophic. So, why did this happen so many times? They give a few reasons in the post. First, academic papers usually describe interactive protocols rather than the Fiat-Shamir portion, making it ambiguous how to do it correctly. Second, zkVMs are very modular in structure. So, each layer assumes that the other performs transcript binding of a value. There is also heavy pressure for performance in the prover that makes checks nice to remove. The fix is simple for each implementation: just bind the value to the challenge. At a higher-level it's important to make these less error-prone. One idea is to make the proof and the transcript (previous state) equal, so the values are automatically absorbed. Even though I'm not a cryptography person, I still learned a lot from this post and really enjoyed it!
Analysis Summary
# Vulnerability: Unfaithful Claims (Fiat-Shamir Transcript Binding Flaws)
## CVE Details
- **CVE ID**: Not explicitly listed (Internal tracking often used by ZK projects; vulnerabilities were reported to and patched by vendors).
- **CVSS Score**: 10.0 (Critical - estimated based on total protocol bypass)
- **CWE**: CWE-347: Improper Verification of Cryptographic Signature / CWE-327: Use of a Broken or Risky Cryptographic Algorithm (Specifically: Fiat-Shamir Implementation Error).
## Affected Systems
- **Products**: Various Zero-Knowledge Virtual Machines (zkVMs) and Verifiable Computing systems.
- **Versions**: Versions prior to March 2026 (Contact specific vendors for patch commit hashes).
- **Affected Implementations**:
1. **Jolt**
2. **Nexus**
3. **Cairo-M**
4. **Ceno**
5. **Expander**
6. **Binius64**
## Vulnerability Description
The flaw stems from a failure to correctly implement the **Fiat-Shamir heuristic**, which converts interactive proofs into non-interactive ones by hashing the protocol's transcript to generate "random" challenges.
In these six implementations, prover-supplied values (such as `claimed_sum` or `opening_claim`) that directly affect verification equations were not **absorbed** (hashed) into the transcript before the verifier generated the challenges. This violates a core cryptographic invariant: the hash must include everything that affects verification *before* the challenges derived from it are used. Because the challenges were generated without binding these values, an attacker can "grind" or solve for values after seeing the challenges, allowing them to manipulate the verification equations to accept invalid state transitions or false statements.
## Exploitation
- **Status**: PoC available (Researchers hosted challenges at `jolt.chal.osec[.]io` and `nexus.chal.osec[.]io`).
- **Complexity**: High (Requires advanced understanding of ZK polynomial constraints and the ability to solve for specific values in the verification equation).
- **Attack Vector**: Network (The attacker submits a malicious proof payload to a verifier/blockchain).
## Impact
- **Confidentiality**: Low (The bug targets soundness, not necessarily privacy).
- **Integrity**: **Very High** (Total bypass of the proof system; allows proving mathematically impossible statements or forging transactions).
- **Availability**: Medium (Can result in the corruption of a blockchain state).
## Remediation
### Patches
The research team reported these findings to the respective maintainers. All six projects have reportedly implemented fixes.
- **Jolt**: Patch applied to the transcript binding logic.
- **Nexus**: Patch applied to the Fiat-Shamir absorption sequence.
- **Cairo-M/Ceno/Expander/Binius64**: Updates available; users should update to the latest repository versions.
### Workarounds
- **Auditing**: Manually verify that every prover-controlled value in the proof payload is absorbed into the transcript before any `squeeze` (challenge generation) occurs.
- **Design Change**: Adopt a "transcript-as-state" model where the proof and the transcript are identical, ensuring all values are automatically absorbed.
## Detection
- **Indicators of Compromise**: Proofs that contain "mathematically impossible" results (e.g., false mathematical statements) that nonetheless pass verification.
- **Detection Methods**: Specialized ZK auditors must trace the data flow of the prover-supplied values to ensure they are hashed before the verifier samples any points for constraint checking.
## References
- Original Advisory: `https://osec[.]io/blog/2026-03-03-zkvms-unfaithful-claims/`
- Researcher Twitter (X): `https://x[.]com/0xdeuterium`
- Researcher Twitter (X): `https://x[.]com/_Blupper_`