CVE-2024-45039 is a medium-severity (CVSS 6.2) Information Exposure vulnerability in github.com/consensys/gnark. A fix is available for github.com/consensys/gnark — see the affected versions and patch details below.
gnark's Groth16 commitment extension unsound for more than one commitment
Exploitation Status
Proof-of-concept exploit code exists
- CISA’s SSVC triage found public proof-of-concept exploit code for this CVE, though no confirmed active exploitation.
Exploitation and automatability from CISA’s SSVC triage for CVE-2024-45039.
EPSS Exploitation Probability
EPSS (Exploit Prediction Scoring System) is a daily probability model maintained by FIRST.org. It estimates the likelihood a CVE will be exploited in production environments within the next 30 days, derived from real-world threat intelligence signals.
How urgent is this, really
CVE-2024-45039 plotted by exploitation likelihood (EPSS) against impact (CVSS). The shaded corner — EPSS 50%+ and CVSS 7.0+ — is where this CVE doesn't sit, though severity or exploitability alone can still warrant action.
Where this sits among everything scored
Of 378,567 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Real counts from FIRST.org, not a sample — log-scaled since the landscape is heavily right-skewed.
Real-World Exposure
github.com/consensys/gnarkReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects Go packages — download data is not available via public APIs for these ecosystems.
Description
Description
The summary is that the proof of knowledge associated to a commitment is crucial to bind the commitment to the actual circuit variables that were supposed to be committed. However, the same σ is used for all proofs of knowledge for the commitments, which allows mixing between them, making it possible to fix the value of all but one commitment before choosing the circuit variable assignments.
In more detail:
To simplify notation, let us consider the case of two commitments, each to only a single variable. Let's say the basis elements for those commitments are K_0 and K_1. Then the proving key will contain K_0 and K_1, and also σ*K_0 and σ*K_1 for the proof of knowledge. The honest prover assigning a to the first circuit variable and b to the second will then produce commitments
D_0 = a*K_0
D_1 = b*K_1
Out of the two D's, a challenge r for the commitment folding will be generated. The folded commitment will then be
D_folded = D_0 + r*D_1 = a*K_0 + r*b*K_1
The honest prover will supply a fitting proof of knowledge
P = a*(σ*K_0) + r*b*(σ*K_1)
Now the verifier will only use all of this in two ways:
- In the check of the Groth16 proof itself, where only the sum
D_0 + D_1is used. - In the proof of knowledge check, where it will be verified that P is indeed
σ*(D_0 + r*D_1), with r calculated fromD_0andD_1as before.
This has the following implications. In the following, a malicious prover's points will have an apostrophe appended, and we keep D_0 etc. for the legitimate values:
- A malicious prover is only forced to provide
D'_0andD'_1such that the sum is correct. So they can use arbitraryD'_0as long as they setD'_1 = D_0 + D_1 - D'_0. - After choosing
D'_0andD'_1, the prover can always calculate r. Evaluatingσ*(D'_0 + r*D'_1)is then possible as long as bothD'_0andD'_1are linear combinations of basis elements for which σ times that basis element is known. In particular, this works as long asD'_0andD'_1are linear combinations ofK_0andK_1.
The upshot is that a malicious prover can choose arbitrary a' and b', and then set
D'_0 = a'*K_0 + b'*K_1
D'_1 = (a - a')*K_0 + (b - b')*K_1
Then they calculate r for this, and set
P = (a' + r*(a-a'))*(σ*K_0) + (b' + r*(b-b'))*(σ*K_1)
This will then be accepted as a valid proof. Yet the first commitment point can be chosen completely independently of a and b, so in particular the malicious prover can use a constant for this, so that they will know the in-circuit challenge that will be added to the public inputs before they have to choose the witness assignments. For most use cases of such challenges (for proving things with Fiat-Shamir, random linear combinations etc.) this causes a critical soundness problem.
The problem generalizes to more than two commitments and commitments to more than one circuit variable each; one can freely choose all but one commitment as arbitrary linear combinations of the basis elements for all commitments, and then must choose the one remaining commitment in such a way that the sum is correct.
The root cause of the issue is that the σ used for the proofs of knowledge is the same, allowing to mix between the basis elements, as one has σ times them available for all of them.
So the fix is to have a separate σ for each commitment. So in our example above, the proving key would have the basis elements K_0 and K_1, and for the proofs of knowledge now σ_0*K_0 and σ_1*K_1. Folding the commitments would not be possible in the same way now, so the verifier will have to do more pairings. The prover could still provide a folded proof of knowledge however. With
D_0 = a*K_0
D_1 = b*K_1
the proof of knowledge would be
P = a*(σ_0*K_0) + r*b*(σ_1*K_1)
For later, let us use notation for the unfolded proofs of knowledge
P_0 = a*(σ_0*K_0)
P_1 = b*(σ_1*K_1)
so that
P = P_0 + r*P_1
The verifying key would need G and σ_0*G and σ_1*G. To check the two unfolded proofs of knowledge would be the checks
e(P_0, G) = e(D_0, σ_0*G)
e(P_1, G) = e(D_1, σ_1*G)
As r is a challenge derived from D_0 and D_1, we may instead check
e(P_0, G) + r*e(P_1, G) = e(D_0, σ_0*G) + r*e(D_1, σ_1*G)
The left hand side is
e(P_0, G) + r*e(P_1, G) = e(P_0 + r*P_1, G) = e(P, G)
So the prover can just provide P and then the verifier checks
e(P, G) = e(D_0, σ_0*G) + r*e(D_1, σ_1*G)
Unfortunately, the right hand side can't be folded as before, as there isn't a side of the pairing that is kept constant between the pairings as before. So the verifier will need to have a pairing for each commitment on the right hand side.
Impact
It is a soundness issue - in case of multiple commitments used inside the circuit the prover is able to choose all but the last commitment. As we use the commitments for optimized non-native multiplication, lookup checks etc. as random challenges, then it could impact the soundness of the whole circuit.
However, using multiple commitments has been discouraged due to the additional cost to the verifier and it has not been supported in the recursive in-circuit Groth16 verifier and Solidity verifier. So we expect the impact of the issue be very small - only for the users who have implemented the native Groth16 verifier or are using it with multiple commitments. We do not have information of such users.
Patches
The issue has been patched in e7c66b000454f4d2a4ae48c005c34154d4cfc2a2
Workarounds
The recommendation has been to use only a single commitment and then derive in-circuit commitments as needed using std/multicommit package.
References
See the correspondence above.
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐹Go | github.com/consensys/gnark | all versions | 0.11.0go get github.com/consensys/gnark@v0.11.0 |
Detection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for github.com/consensys/gnark, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update github.com/consensys/gnark to 0.11.0 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms CVE-2024-45039 is resolved across your whole dependency graph.
Workarounds
If you can't upgrade right away: gate or disable the affected feature, validate untrusted input at the boundary, and avoid passing attacker-controlled data into the vulnerable path. O3's runtime protection blocks exploitation in production as an interim safeguard until the upgrade lands.
How O3 protects you
O3 Security's impact-aware SCA analyses which vulnerable code paths your application actually calls, so a match like CVE-2024-45039 can be triaged on real exposure rather than presence alone.
Tailored to CVE-2024-45039. Runtime protection reduces exposure until a permanent patch is applied and verified — it complements patching, it doesn't replace it.
Frequently Asked Questions
Is CVE-2024-45039 in your dependencies?
O3 Security finds CVE-2024-45039 across Go dependencies, including transitive ones, and its impact-aware SCA ranks findings by whether your code actually calls the vulnerable path.