Severity by source
CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:H/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Network-reachable prover input with no auth or UI; AC:H because crafting transcript collisions is non-trivial; integrity-only impact on the verifier with scope change to relying systems.
Primary rating from Vendor (https://github.com/Plonky3/Plonky3).
CVSS VectorVendor: https://github.com/Plonky3/Plonky3
CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:H/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
Lifecycle Timeline
6DescriptionCVE.org
Impact
- Key:
challenger/src/multi_field_challenger.rs|MultiField32Challenger::duplexing|transcript_malleability - Affected files:
challenger/src/multi_field_challenger.rs,field/src/helpers.rs - Violated invariant: The Fiat-Shamir sponge must bind challenges to the exact sequence of observed field elements. Specifically: (1) absorption must be injective - distinct observation streams must produce distinct sponge states, (2) squeezing must be injective - distinct PF rate cells must yield distinct F challenge sequences, and (3) all bits of each absorbed PF element must influence the sponge state.
- Exploit scenario: An attacker controlling prover-side observations can craft distinct transcripts that produce identical challenges, breaking the binding property of Fiat-Shamir. Three independent attack vectors exist:
- Partial-chunk aliasing (absorb):
duplexing()packsinput_buffer.chunks(num_f_elms)viareduce_32(base 2^32) with no length marker and no zeroing of unused rate slots. Observing[x]followed by a sample yields the same sponge state as[x, 0, ..., 0](padded tonum_f_elms) followed by a sample, sincereduce_32treats missing high limbs identically to explicit zeros. The attacker can extend or truncate the tail of any observation batch without changing future challenges. - Non-injective squeeze (squeeze):
split_32decomposes each PF rate cell into base-2^64 digits and maps each throughTF::from_u64, which reduces modF::ORDER(~2^31). Two distinct PF values whose base-2^64 digits differ only in their upper 33 bits produce identical F challenge sequences. This weakens the entropy of sampled challenges and can enable selective forgery when the attacker can influence the sponge state pre-squeeze. - High-bit truncation (observe Hash/MerkleCap):
num_f_elms = PF::bits() / 64computes the number of F limbs per PF element. For BN254 (254-bit field), this yields 3 limbs covering 192 bits - the top 62 bits of every digest word are silently discarded. An attacker can find two distinct BN254 hash digests that differ only in bits 192-253 and observe them interchangeably without affecting challenges.
- Evidence: In
duplexing(), the absorb path (reduce_32with base 2^32) and the squeeze path (split_32with base 2^64) use incompatible radices with no length domain separation.reduce_32is a plain Horner foldacc * 2^32 + digitwith no padding or tag, so trailing zeros are free.split_32extracts u64 digits and casts each viaTF::from_u64, which performs modular reduction, collapsing the top bits. The limb countPF::bits() / 64is a floor division that silently drops all bits beyond64 * num_f_elmsfor fields whose bit-width is not a multiple of 64.
Patches
Included in v0.4.3 and v0.5.3
AnalysisAI
Transcript malleability in the Plonky3 p3-challenger crate (MultiField32Challenger) allows a malicious prover to craft distinct observation streams that collapse to identical Fiat-Shamir challenges, breaking the soundness binding of the SNARK transcript. Affected versions are p3-challenger < 0.4.3 and 0.5.0-0.5.2, with fixes shipped in 0.4.3 and 0.5.3. No public exploit identified at time of analysis, and the issue is not listed in CISA KEV; risk is primarily to ZK proof system integrity rather than confidentiality.
Technical ContextAI
Plonky3 is a Rust toolkit for building zk-SNARKs and STARKs, and p3-challenger implements the Fiat-Shamir challenger that derives verifier challenges from observed transcript elements. The MultiField32Challenger::duplexing routine bridges a small 32-bit base field F and a larger permutation field PF (e.g., BN254) using reduce_32 (base 2^32 Horner fold) for absorption and split_32 (base 2^64 with modular reduction via TF::from_u64) for squeezing. CWE-345 (Insufficient Verification of Data Authenticity) applies because the sponge lacks length domain separation and padding: absorption is non-injective due to free trailing zeros, squeezing is non-injective because the upper 33 bits of each 2^64 digit are reduced mod F::ORDER (~2^31), and num_f_elms = PF::bits()/64 floor-divides, silently discarding the top 62 bits of BN254 (254-bit) digest words.
RemediationAI
Vendor-released patch: upgrade p3-challenger to 0.4.3 if on the 0.4.x line, or to 0.5.3 if on the 0.5.x line, as published in GHSA-vj64-rjf3-w3v7 (https://github.com/Plonky3/Plonky3/security/advisories/GHSA-vj64-rjf3-w3v7). For deployments that cannot upgrade immediately, the only meaningful compensating control is to avoid MultiField32Challenger with permutation fields whose bit-width is not a multiple of 64 (in particular BN254) and to refuse proofs whose transcript could include partial-chunk observation batches - both of which materially reduce functionality of the proof system and should be considered stopgaps only. Re-verifying any historical proofs produced under the vulnerable challenger is advisable where audit integrity matters, since malleable transcripts may have been accepted.
Same technique Code Injection
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-36119
GHSA-vj64-rjf3-w3v7