CVE-2026-54496: halo2_gadgets Variable-Base Scalar Multiplication Under-Constrained Base Point
A missing copy constraint in Zcash's halo2_gadgets ECC gadget let a malicious prover substitute an arbitrary base point into the Orchard Action circuit, bypassing the integrity check that ties a…
The problem
The variable-base scalar multiplication gadget in halo2_gadgets/src/ecc/chip/mul/incomplete.rs wrote the base point coordinates (x_p, y_p) into the circuit using assign_advice() at the first iteration of the double-and-add loop (L309-310, commit 32a87582).
That call places a witness value in the constraint system but does not tie it to any previously committed cell.
The existing q_mul_2 selector kept x_p/y_p uniform across loop rows, but it anchored them only to the first row's unconstrained witness. A malicious prover could therefore set (x_p, y_p) to any point they chose. The gadget enforces the Orchard diversified-address-integrity condition pk_d = [ivk] g_d; with a free base, an attacker could pick a fake nullifier key nk, derive a wrong ivk from it, then solve for the base point that makes [ivk] g_d equal the target pk_d, defeating the check entirely.
The result was undetectable double-spending or theft of funds protected only by an incoming viewing key.
Proof of concept
A working proof-of-concept for CVE-2026-54496 in zebrad, with the exact payload below.
// Vulnerable path: assign_advice does NOT create a copy constraint.
// Attacker controls (x_p, y_p) freely in the first loop iteration.
//
// incomplete.rs (pre-fix, ~L309-310)
region.assign_advice(
|| "x_p",
self.double_and_add.x_p,
row + offset, // first iteration row
|| x_p, // attacker sets this to a crafted point
)?;
region.assign_advice(
|| "y_p",
self.y_p,
row + offset,
|| y_p, // attacker sets this to a crafted point
)?;
// Exploit algebra (pseudocode):
// 1. Pick any fake nk (not the note's real nullifier key).
// 2. Derive wrong_ivk = Commit_ivk(Extract(ak), nk) -- circuit forces this honestly.
// 3. Solve for crafted_base such that [wrong_ivk] crafted_base == pk_d.
// (Feasible because ECDLP is over a known-order group; prover knows wrong_ivk.)
// 4. Set x_p = crafted_base.x, y_p = crafted_base.y in the witness.
// 5. q_mul_2 propagates the chosen base across all loop rows -> proof verifies.
// 6. nf = Nullifier(nk, ...) produces a fresh, valid-looking nullifier for the
// same note, enabling a repeat spend.CWE-345 (Insufficient Verification of Data Authenticity). In halo2's PLONKish constraint system, assign_advice() places a witness value into a cell but creates no permutation constraint linking that cell to any other. The q_mul_2 custom gate constrained consistency across loop iterations but had no anchor to the actual base point passed in by the caller.
Replacing the two assign_advice() calls with copy_advice() inserts a copy constraint (a permutation argument) that forces the first-row cell to equal the cell holding the real base, which q_mul_2 then propagates transitively to every row of the loop. Because changing the circuit changes the verifying key, the fix required a hard fork (NU6.2) to rotate the pinned key on-chain.
The fix
Upgrade to halo2_gadgets >= 0.5.0, orchard >= 0.14.0, zcash_primitives >= 0.28.0, zebrad >= 5.0.0 (NU6.2, activated block 3,364,600 on 2026-06-03), or zcashd >= 6.20.0. An interim soft-fork mitigation that rejected all Orchard-containing transactions shipped in zebrad 4.5.3 (block 3,363,426).
Downstream projects using assign_advice() to supply a value that must equal an already-committed cell should audit those sites and replace them with copy_advice() or an explicit equality constraint.
Reported by Taylor Hornby (independent, contracted by Shielded Labs).
Related research
- high · 7.5CVE-2026-16756CVE-2026-16756: aws-smithy-http-server Slowloris Denial of Service
- high · 7.5CVE-2026-25800: quinn-proto Unbounded Stream Reassembly Memory Exhaustion
- criticalexploration: Malicious Remote Code Execution via Embedded Downloader
- highCVE-2026-53530CVE-2026-53530: ratex-parser Process Abort via UTF-8 Multibyte Delimiter in \verb