Feb 15, 2026·6 min read·8 visits
Triton VM failed to include the FRI polynomial and trace height in the transcript hash. This broke the Fiat-Shamir binding, allowing attackers to retroactively choose values that satisfy the verifier's challenges, effectively forging proofs for invalid computations.
A critical soundness vulnerability in Triton VM allows attackers to forge Zero-Knowledge proofs by exploiting a flaw in the Fiat-Shamir transform implementation. By failing to absorb specific polynomial coefficients into the cryptographic sponge before generating challenges, the verifier allows a malicious prover to craft the proof after seeing the 'random' challenges.
Zero-Knowledge Virtual Machines (ZK-VMs) are the alchemy of modern cryptography. They promise to let you run a program, produce a tiny cryptographic receipt (the proof), and convince anyone that the computation was correct without revealing the inputs. It's magic, but magic relies on strict rules. In the world of STARKs (Scalable Transparent Arguments of Knowledge), the most sacred rule is Fiat-Shamir.
Imagine a magician (the Prover) asking a volunteer (the Verifier) to pick a card. If the magician forces the volunteer to pick the Ace of Spades, it's not a trick; it's a scam. In non-interactive ZK proofs, we replace the volunteer with a cryptographic hash function (a 'random oracle' or 'sponge'). The Prover dumps their work into the sponge, and the sponge squeezes out random challenges. Because the challenges depend on the work, the Prover can't predict them.
Triton VM, a Rust-based ZK-VM, built a sophisticated sponge using the Tip5 hash function. But in version < 2.0.0, the sponge had a little case of selective amnesia. It forgot to look at the Prover's final homework before handing out the grade. And as any cheater knows, if the teacher hands you the answer key before you turn in the test, you're going to get an A+.
The vulnerability lies deep within the FRI (Fast Reed-Solomon Interactive Oracle Proof of Proximity) protocol implementation. FRI is the heavy lifting engine that proves a polynomial has a low degree. In a proper implementation, every single coefficient and commitment sent by the Prover must be absorbed into the transcript (the sponge state) before the Verifier generates the next challenge.
Triton VM uses a macro called proof_items! to define what gets absorbed. In the vulnerable versions, two critical items were explicitly set to exclude themselves from the sponge:
FriPolynomial: The coefficients of the final polynomial.Log2PaddedHeight: The size of the execution trace.Because these values were not absorbed, the random challenges generated by the sponge did not depend on them. This creates a causality loop break. The Prover can compute the challenges first, and then choose a FriPolynomial that happens to satisfy those specific challenges. It effectively turns the system from "I promise this polynomial is valid" to "I found a polynomial that matches the 3 random points you just asked about."
The fix is embarrassingly simple, which highlights just how fragile ZK implementations can be. The vulnerability wasn't a buffer overflow or a complex logic error; it was a configuration flag in a Rust macro.
In triton-vm/src/proof_item.rs, the proof_items! macro defines how different parts of the proof interact with the Fiat-Shamir sponge. Look at the diff below. The boolean value indicates whether the item is included in the Fiat-Shamir heuristic (hashed).
Vulnerable Code:
// The 'false' here means: "Don't add this to the hash state"
Log2PaddedHeight(u32) => false, try_into_log2_padded_height,
FriPolynomial(Polynomial<'static, XFieldElement>) => false, try_into_fri_polynomial,Patched Code (Commit 3a045d636...):
// The 'true' binds the prover to these values BEFORE challenges are issued
Log2PaddedHeight(u32) => true, try_into_log2_padded_height,
FriPolynomial(Polynomial<'static, XFieldElement>) => true, try_into_fri_polynomial,By flipping false to true, the developers ensured that these values alter the sponge state. If a malicious prover tries to change the polynomial later, the sponge state would be different, resulting in different challenges, invalidating their forged solution.
To exploit this, we don't need to break encryption. We just need to abuse the order of operations. Here is how a malicious Prover forges a proof for an invalid state transition (e.g., minting 1,000,000 tokens out of thin air):
FriPolynomial.FriPolynomial isn't hashed, the sponge state depends only on previous items. We can locally calculate exactly what indices (challenges) the verifier will query.true.This is a soundness error. In ZK terms, soundness is the guarantee that false statements cannot be proven. With this vulnerability, that guarantee evaporates. If Triton VM is used as the validity proof for a blockchain rollup, an attacker could submit a proof that validates an invalid block—stealing funds, corrupting state, or censoring transactions.
Because the exploit is algorithmic, it is 100% reliable. There is no race condition, no heap grooming, and no luck involved. If you are running a version prior to 2.0.0, any proof you verify is essentially worthless against a sophisticated adversary. The severity is effectively maximum for the context of a ZK system.
The Triton VM team released version 2.0.0 to address this. The patch not only flips the inclusion flags to true but also increments the protocol version number, effectively invalidating all old proofs (since they were generated with a broken Fiat-Shamir transform).
If you are using triton-vm, update your Cargo.toml immediately:
[dependencies]
triton-vm = "2.0.0"Additionally, the patch introduced sanity checks for log2_padded_height to prevent integer overflows, closing the door on a potential Denial of Service vector where a prover could claim an impossibly large execution trace.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
Triton VM TritonVM | < 2.0.0 | 2.0.0 |
| Attribute | Detail |
|---|---|
| CWE | CWE-345: Insufficient Verification of Data Authenticity |
| Attack Vector | Network (Proof Submission) |
| Impact | Proof Forgery / Integrity Compromise |
| CVSS v3.1 | 9.8 (Critical) |
| Affected Component | Fiat-Shamir Transform (FRI Protocol) |
| Patch Commit | 3a045d636e97bb2eb628671db0001aa665c19dd8 |