{"id":"GHSA-rhr8-3pm8-cq29","summary":"Forge: ASN.1 resource exhaustion via BIT STRING decoding in asn1.fromDer","details":"### Summary\n`asn1.fromDer()` decodes the contents of BIT STRINGs as ASN.1 by default. That decode ignored the `maxDepth` recursion limit (an incomplete fix of CVE-2025-66031) and also decoded nested BIT STRINGs, keeping a full copy of the decoded contents at each level, so small crafted DER or PEM inputs (certificates, keys, PKCS structures) use excessive CPU time and memory and can abort the Node.js process. Fixed in node-forge 1.4.1.\n\n### Details\n`asn1.fromDer()` attempts by default to decode the contents of primitive BIT STRINGs as ASN.1 (`decodeBitStrings`, default `true`). Two flaws in that decode in `lib/asn1.js` allow resource exhaustion:\n\n1. `_fromDer()` started the BIT STRING decode with a new options object, `{strict: true, decodeBitStrings: true}`, that did not include `maxDepth`. Inside that decode the depth check compared against `undefined` and never triggered, so the recursion limit added for CVE-2025-66031 (GHSA-554w-wpv2-vw27) was not enforced for anything below a decoded BIT STRING. Both the global `asn1.maxDepth` (default 256) and the per-call `maxDepth` option were ignored there. Errors in this speculative decode, including stack overflows, are caught and the contents kept as raw bytes, so deep nesting is parsed instead of rejected. This is an incomplete fix of CVE-2025-66031.\n2. BIT STRINGs nested inside decoded BIT STRING contents were decoded as well, and `asn1.create()` gives each decoded BIT STRING a deep copy of its decoded contents (`obj.original`, used by `toDer()` to detect changes). Memory use therefore grows quadratically with the nesting depth, even within the default depth limit.\n\nNo specification is violated; the parser lacks a resource limit.\n\nThe fix passes `maxDepth` to the BIT STRING decode, and decodes BIT STRING contents with `decodeBitStrings: false`, so only one level of BIT STRING contents is decoded. Contents that would exceed the limit, and BIT STRINGs inside decoded BIT STRING contents, are kept as undecoded BIT STRING values; `toDer()` writes their original bytes. Common structures such as SubjectPublicKeyInfo only use one level. Code that relied on nested decoding can decode the inner value explicitly with `asn1.fromDer()`.\n\n#### Patches\nFixed in node-forge 1.4.1. The `maxDepth` limit now applies to ASN.1 decoded from BIT STRING contents, and only one level of BIT STRING contents is decoded.\n\n#### Workarounds\nCode that calls `forge.asn1.fromDer()` directly and does not need encapsulated BIT STRING contents decoded can pass `{decodeBitStrings: false}`. This changes the parsed structure. The higher-level PKI, PKCS and TLS APIs do not expose this option and rely on the decoded form. For those, the only remedy is upgrading.\n\n#### References\n- CVE-2025-66031 / https://github.com/digitalbazaar/forge/security/advisories/GHSA-554w-wpv2-vw27 (ASN.1 Unbounded Recursion, fixed in 1.3.2)\n- https://cwe.mitre.org/data/definitions/674.html (CWE-674: Uncontrolled Recursion)\n- https://cwe.mitre.org/data/definitions/400.html (CWE-400: Uncontrolled Resource Consumption)\n\n#### Severity\nCVSS 3.1: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H (7.5, High)\nCVSS 4.0 (informational): CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N (8.7)\nBoth versions rate this High. The severity rating follows CVSS 3.1.\n\n#### Credits\nReported by Ethan Kim (cremit.io), Youngjoon Kim (@acorn421) and Brian Lee (@hibrian827) of Georgia Tech SSLab, @arpitjain099, and Mike Read.\n\n### Proof of concept\nInput shape: n nested primitive BIT STRINGs (`03 \u003clen\u003e 00 \u003cinner\u003e`) around a NULL. For example:\n\n    var asn1 = forge.asn1;\n    var obj = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.NULL, false, '');\n    for(var i = 0; i \u003c 2000; ++i) {\n      obj = asn1.create(asn1.Class.UNIVERSAL, asn1.Type.BITSTRING, false, [obj]);\n    }\n    var der = asn1.toDer(obj).getBytes(); // 9,886 bytes\n    asn1.fromDer(der, {maxDepth: 10});\n\nOn 1.4.0 (Node.js 24) this parses all 2,000 levels despite `maxDepth: 10`, taking about 0.5-1 s and 600 MB. One SEQUENCE holding 20 such chains (about 198 KB) aborts a process with the default heap with \"FATAL ERROR: JavaScript heap out of memory\", which the application cannot catch. Within the depth limit, 80 chains of 254 levels (92 KB) still use about 0.8 GB. The same inputs wrapped as PEM reach `pki.publicKeyFromPem()` and `pki.certificateFromPem()` (a 20 KB certificate PEM used 1.3 GB before being rejected). On 1.4.1 these inputs parse in a few milliseconds and about 55-65 MB.\n\nRegression tests in tests/unit/asn1.js: \"should limit depth in decoded BIT STRINGs (default)\", \"should limit depth in decoded BIT STRINGs (maxDepth)\", \"should only decode one level of BIT STRINGs\" and \"should not decode BIT STRINGs in many BIT STRINGs\".\n\n### Impact\nA remote, unauthenticated attacker can supply a small DER or PEM structure with nested BIT STRINGs to any API that parses with `asn1.fromDer()` using default options. This includes `pki.certificateFromPem()`, `pki.publicKeyFromPem()`, the PKCS#7, PKCS#8 and PKCS#12 loaders, and server certificates received by forge's TLS client. Parsing such input consumes excessive CPU time and memory before the structure is rejected. On Node.js 24 a 10 KB input used about 600 MB and a 25 KB input about 1.4 GB. Inputs of about 25 KB abort a process limited to a 512 MB heap, and about 200 KB abort a process with the default heap, with a fatal out-of-memory error that the application cannot catch. Inputs that stay within the depth limit still reach about 0.8 GB at 92 KB and 1.8 GB at 231 KB. The result is denial of service. There is no confidentiality or integrity impact.","aliases":["CVE-2026-119191"],"modified":"2026-11-10T16:15:05.495721590Z","published":"2026-11-10T15:57:41Z","database_specific":{"severity":"HIGH","github_reviewed":true,"github_reviewed_at":"2026-11-10T15:57:41Z","nvd_published_at":"2026-10-29T20:17:30Z","cwe_ids":["CWE-400","CWE-674"]},"references":[{"type":"WEB","url":"https://github.com/digitalbazaar/forge/security/advisories/GHSA-rhr8-3pm8-cq29"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-119191"},{"type":"WEB","url":"https://github.com/digitalbazaar/forge/commit/24035c181f41885ea3ec7279e6c0fcf270b6ec4b"},{"type":"WEB","url":"https://github.com/digitalbazaar/forge/commit/523c8e74c28457daeff044230dce4a36313c5ab7"},{"type":"PACKAGE","url":"https://github.com/digitalbazaar/forge"},{"type":"WEB","url":"https://github.com/digitalbazaar/forge/releases/tag/v1.4.1"}],"affected":[{"package":{"name":"node-forge","ecosystem":"npm","purl":"pkg:npm/node-forge"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0"},{"fixed":"1.4.1"}]}],"database_specific":{"last_known_affected_version_range":"\u003c= 1.4.0","source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/11/GHSA-rhr8-3pm8-cq29/GHSA-rhr8-3pm8-cq29.json"}}],"schema_version":"1.9.2","severity":[{"type":"CVSS_V3","score":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H"}]}