{"id":"CVE-2026-118782","aliases":["GHSA-jc66-7q78-782g"],"title":"Forge: PKCS#12 MAC forgery and constant-key PBE via zero iteration count","titleSource":"GitHub_M","descriptionSource":"OSV","declaredSeverity":"HIGH","description":"### Summary\n\nforge.pkcs12.pkcs12FromAsn1() accepts a PKCS#12 MAC iteration count of zero (or less) from the input, and the PKCS#12 key derivation function then returns a constant key that does not depend on the password or salt. An attacker who does not know the password can modify a PKCS#12/PFX file and compute a MAC that verifies with any password; PKCS#12 PBE-encrypted private keys prepared the same way decrypt with any password. Fixed in node-forge 1.4.1.\n\n### Details\n\n`p12.pkcs12FromAsn1()` in `lib/pkcs12.js` reads the MAC iteration count (`MacData.iterations`) from the input and passes it to the PKCS#12 key derivation function `pki.pbe.generatePkcs12Key()` in `lib/pbe.js` (also exposed as `forge.pkcs12.generateKey()`) without checking that it is at least 1. The key derivation function (RFC 7292 Appendix B.2) runs its hash loop `iter` times. With an iteration count of 0 (or an empty or negative INTEGER), no hashing is done and the function returns the PKCS#12 diversifier ID byte repeated, for example `0x03` repeated 20 times for an HMAC-SHA1 MAC key. The result does not depend on the password or the salt, so anyone can compute a valid MAC over arbitrary PFX contents.\n\nThe same key derivation function is used by `pki.pbe.getCipherForPKCS12PBE()` for the PKCS#12 password-based encryption schemes `pbeWithSHAAnd3-KeyTripleDES-CBC` and `pbewithSHAAnd40BitRC2-CBC`, whose `pkcs-12PbeParams` iteration count is also read from the input. With an iteration count of 0 or less, the encryption key and IV are constants (`0x01` and `0x02` repeated). This is reached from shrouded key bags and encrypted SafeContents in `pkcs12FromAsn1()`, and from `forge.pki.decryptPrivateKeyInfo()` (for example for `ENCRYPTED PRIVATE KEY` PEM input).\n\nRFC 7292 defines `MacData` with `iterations INTEGER DEFAULT 1`; an iteration count only makes sense as 1 or more (RFC 8018 uses `INTEGER (1..MAX)` for the same concept). PBES2 (PBKDF2) encrypted content is not affected.\n\nThe fix rejects iteration counts less than 1 when verifying a PKCS#12 MAC in `pkcs12FromAsn1()` (\"PKCS#12 MAC iteration count must be >= 1.\") and at the entry of `pki.pbe.generatePkcs12Key()` (\"PKCS#12 key derivation iteration count must be >= 1.\"), which covers the MAC, both PKCS#12 PBE schemes and direct `forge.pkcs12.generateKey()` callers.\n\n### Patches\nFixed in node-forge 1.4.1. Iteration counts less than 1 are now rejected when verifying a PKCS#12 MAC and in the PKCS#12 key derivation function.\n\n### Workarounds\nBefore calling `forge.pkcs12.pkcs12FromAsn1()`, reject inputs whose `MacData.iterations` is present and less than 1. For encrypted private keys using the PKCS#12 PBE schemes, there is no simple workaround other than checking the `pkcs-12PbeParams` iteration count before decryption, or upgrading.\n\n### Credits\nReported by Thai, Calif.io in collaboration with Claude and Anthropic Research.\n\n### Proof of concept\n\nTake any PKCS#12/PFX file, replace its AuthenticatedSafe with attacker-chosen content (for example the attacker's certificate and an unencrypted private key), set `MacData.iterations` to 0 (DER `02 01 00`), and compute the HMAC-SHA1 over the new content with the key `0x03` repeated 20 times. On node-forge <= 1.4.0, `forge.pkcs12.pkcs12FromAsn1(asn1, password)` reports successful MAC verification for whatever password the application supplies and returns the attacker's certificate and key. An empty or negative iteration count behaves the same way. Likewise, a shrouded key bag or `ENCRYPTED PRIVATE KEY` using `pbeWithSHAAnd3-KeyTripleDES-CBC` with iteration count 0 decrypts with any password.\n\nThe constant key is visible directly:\n\n```js\nvar salt = new forge.util.ByteBuffer('anysalt');\nforge.pkcs12.generateKey('any password', salt, 3, 0, 20).toHex();\n// <= 1.4.0: '0303030303030303030303030303030303030303'\n// 1.4.1: throws \"PKCS#12 key derivation iteration count must be >= 1.\"\n```\n\nThe fix adds regression tests in `tests/unit/pkcs12.js` and the reporter's PoC in `tests/pocs/`.\n\n### Impact\n\nApplications that import PKCS#12/PFX files or PKCS#8 encrypted private keys from untrusted sources with `forge.pkcs12.pkcs12FromAsn1()` or `forge.pki.decryptPrivateKeyInfo()`, and rely on the password-based MAC or on successful decryption to establish that the file was produced by someone who knows the password. The default API and call pattern are affected; no password, oracle or user interaction is needed, only one offline HMAC.\n\nAn attacker can supply a PKCS#12 file with certificates and private keys of their choice (for example, replaced certificates or an unencrypted private key) that node-forge reports as verified under the application's password. The attacker does not learn the password or the original private key. PBES2 (PBKDF2) encrypted content is not affected.","severity":"high","score":7.5,"published":"2026-10-29T18:51:02.388Z","modified":"2026-10-30T23:15:04.512Z","withdrawn":false,"knownExploited":false,"affected":[{"ecosystem":"npm","name":"node-forge","sources":[{"name":"OSV · GHSA-jc66-7q78-782g","url":"https://osv.dev/vulnerability/GHSA-jc66-7q78-782g"}],"ranges":[{"events":[{"introduced":"0"},{"fixed":"1.4.1"}],"type":"SEMVER"}],"versions":[],"fixed":["1.4.1"]}],"sourceAffected":[],"references":["https://github.com/digitalbazaar/forge/security/advisories/GHSA-jc66-7q78-782g","https://github.com/digitalbazaar/forge/commit/d9584aa56824972858366d5187b3d29e46bf3656","https://github.com/digitalbazaar/forge/releases/tag/v1.4.1","https://nvd.nist.gov/vuln/detail/CVE-2026-118782","https://github.com/digitalbazaar/forge"],"sources":[{"name":"CVE","id":"CVE-2026-118782","modified":"2026-10-29T18:51:02.388Z","url":"https://www.cve.org/CVERecord?id=CVE-2026-118782"},{"name":"OSV","id":"GHSA-jc66-7q78-782g","modified":"2026-10-30T23:15:04.512936102Z","url":"https://osv.dev/vulnerability/GHSA-jc66-7q78-782g"}],"assessments":[{"source":"GitHub_M","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","score":7.5},{"source":"OSV","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","score":7.5}],"cve":{"records":[{"id":"CVE-2026-118782","source":"GitHub_M","state":"PUBLISHED","reasons":[],"replacements":[],"disputed":false,"unsupportedWhenAssigned":false,"reserved":"2026-10-28T19:20:05.019Z","published":"2026-10-29T18:51:02.388Z","modified":"2026-10-29T18:51:02.388Z"}],"publishers":[{"name":"GitHub_M","role":"CNA","organizationId":"a0819718-46f1-4df5-94e2-005712e83aaa","modified":"2026-10-29T18:51:02.388Z"}],"references":[{"url":"https://github.com/digitalbazaar/forge/security/advisories/GHSA-jc66-7q78-782g","tags":["x_refsource_CONFIRM"],"source":"GitHub_M"},{"url":"https://github.com/digitalbazaar/forge/commit/d9584aa56824972858366d5187b3d29e46bf3656","tags":["x_refsource_MISC"],"source":"GitHub_M"},{"url":"https://github.com/digitalbazaar/forge/releases/tag/v1.4.1","tags":["x_refsource_MISC"],"source":"GitHub_M"}],"weaknesses":[{"id":"CWE-354","description":"CWE-354: Improper Validation of Integrity Check Value","source":"GitHub_M"},{"id":"CWE-1284","description":"CWE-1284: Improper Validation of Specified Quantity in Input","source":"GitHub_M"}],"applicability":[],"prioritization":[]},"products":[{"vendor":"digitalbazaar","name":"forge","platforms":[],"versions":[{"version":"< 1.4.1","status":"affected"}],"cpes":[],"source":"GitHub_M","url":"https://www.cve.org/CVERecord?id=CVE-2026-118782"}],"remediation":[],"observedAt":"2026-10-30T23:33:07.862Z","kev":{"status":"not-listed","checkedAt":"2026-10-31T10:20:31.211Z"},"coverage":[{"source":"NVD","status":"available","checkedAt":"2026-10-29T19:25:41.052Z","attemptedAt":"2026-10-29T19:25:41.052Z"},{"source":"GHSA","status":"no-result","checkedAt":"2026-10-29T19:25:39.760Z","attemptedAt":"2026-10-29T19:25:39.760Z"},{"source":"EUVD","status":"available","checkedAt":"2026-10-29T19:25:40.843Z","attemptedAt":"2026-10-29T19:25:40.843Z"},{"source":"EPSS","status":"no-result","checkedAt":"2026-10-29T19:25:41.434Z","attemptedAt":"2026-10-29T19:25:41.434Z"}],"evidence":[{"source":"ENISA EUVD","id":"EUVD-2026-113341","url":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-113341","modified":"Oct 29, 2026, 6:51:02 PM","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","checkedAt":"2026-10-29T19:25:40.843Z"},{"source":"NVD","id":"CVE-2026-118782","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-118782","modified":"2026-10-29T19:17:20.983","score":7.5,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","checkedAt":"2026-10-29T19:25:41.052Z"}],"aiSummary":{"text":"The Forge library's PKCS#12 implementation accepts a MAC iteration count of zero, allowing an attacker to modify a PKCS#12/PFX file and compute a valid MAC with any password. This affects node-forge versions less than 1.4.1. An attacker can supply a PKCS#12 file with chosen certificates and private keys that node-forge reports as verified under the application's password.","model":"@cf/meta/llama-3.3-70b-instruct-fp8-fast","generatedAt":"2026-10-31T00:06:20.756Z","sources":[{"name":"CVE","url":"https://www.cve.org/CVERecord?id=CVE-2026-118782"},{"name":"OSV","url":"https://osv.dev/vulnerability/GHSA-jc66-7q78-782g"}]}}