Skip to main content
VulnerabilityPublished

CVE-2026-119196: Ansible@10 · pyasn1 has quadratic CPU consumption in constructed BIT STRING decoding

Affects ansible@10Source GCVE Database

0
Compare

Mentioned vulnerabilities

Authoritative record:CVE-2026-119196· High 7.5

Not known to be exploited · CVSS 7.5 High.

Data as of 31 Oct 2026, 16:51 UTC

CVSS 3.1 base score
7.5High
How severe the flaw is on the 0–10 CVSS scale.

Thin evidence

One scorer or none
CVSS scores by source
CNA (GitHub_M)CVSS 3.17.5
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H

Exploitation evidence

  1. Disclosed: evidence found.

    CVE published 29 Oct 2026

  2. Detectable: no evidence on record.

    No public nuclei template indexed

  3. Weaponized: no evidence on record.

    No public exploit code indexed in Metasploit or Exploit-DB

  4. Exploited in the wild: no evidence on record.

    Not in CISA KEV and no source reports exploitation

  5. Observed by OffSeq Mirage: no evidence on record.

    No attack traffic recorded by OffSeq Mirage sensors

A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 30 Oct 2026.

What to do

Affected software

ProductAffectedFixed in
Homebrewansible@10pkg:brew/ansible%4010Other threatsAffected
  • >=10.6.0
Fixed in–

Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.

Description

## Summary In pyasn1 through 0.6.4, decoding a constructed BIT STRING with many fragments repeatedly shifts and copies the entire value accumulated so far. For fixed-size fragments, CPU time grows quadratically with the encoded input size. An application that passes sufficiently large, attacker-controlled ASN.1 data to an affected decoder can suffer CPU exhaustion during `decode()`; no conversion or formatting of the decoded value is required. The BER and CER decoders reach this path for constructed BIT STRING values, with or without an `asn1Spec`. The DER decoder rejects constructed BIT STRING in schema-less decoding, but its schema-driven path accepts a **definite-length** constructed BIT STRING when given a matching `asn1Spec` and reaches the same vulnerable code. Constructed BIT STRING is not valid DER; this DER exposure is due to decoder permissiveness. DER rejects the indefinite-length form. This is a separate decoder path from the OBJECT IDENTIFIER accumulation fixed in [CVE-2026-59885](https://github.com/advisories/GHSA-8ppf-4f7h-5ppj). ## Technical details In the 0.6.4 [`BitStringPayloadDecoder`](https://github.com/pyasn1/pyasn1/blob/v0.6.4/pyasn1/codec/ber/decoder.py#L173-L301), both the [definite-length](https://github.com/pyasn1/pyasn1/blob/v0.6.4/pyasn1/codec/ber/decoder.py#L237-L255) and [indefinite-length](https://github.com/pyasn1/pyasn1/blob/v0.6.4/pyasn1/codec/ber/decoder.py#L275-L301) fragment loops call `BitString.fromOctetString(..., prepend=bitString)` for every fragment. [`fromOctetString`](https://github.com/pyasn1/pyasn1/blob/v0.6.4/pyasn1/type/univ.py#L614-L627) shifts the growing `SizedInteger` left by the fragment's bit length and ORs in the fragment. Shifting an ever-larger Python integer for each fixed-size fragment makes total work O(n²) for n fragments. The fragments are siblings, so the decoder's nesting-depth limit does not constrain their count; its limit on the number of length octets does not prevent the demonstrated inputs. In 0.6.4, DER's [tag-driven map](https://github.com/pyasn1/pyasn1/blob/v0.6.4/pyasn1/codec/der/decoder.py#L15-L40) disables constructed BIT STRING, while its schema-driven type map retains the inherited decoder that permits it. This explains why a schema-less DER rejection is not a negative result for all DER decoding. Under [X.690](https://www.itu.int/rec/T-REC-X.690-202102-I/en), a conforming CER constructed BIT STRING uses an indefinite-length outer encoding and primitive fragments with 1000 contents octets except possibly the last. That form reaches the affected indefinite-length loop. DER requires primitive string encoding, although the schema-driven pyasn1 decoder accepts the definite-length constructed example below. ## Impact An application is exposed when untrusted input can reach one of the affected decoder paths and its input limits admit enough fragments. The work occupies the decoding thread or worker and may cause denial of service. Whether the input is network-accessible or unauthenticated depends on the calling application. This advisory makes no claim that every application using pyasn1, or any particular protocol implementation, accepts such input. ## Severity assessment (CVSS v3.1) The 7.5 Base score assumes a reasonable application that automatically decodes attacker-supplied ASN.1 from unauthenticated network input. Valid BER and conforming CER values trigger the issue without special conditions, privileges, or user interaction. Repeated crafted values could occupy a finite decoder-worker pool and deny service while the attack continues. No confidentiality or integrity impact is demonstrated. This library-level score does not claim that every application is network-exposed or fully unavailable; deployment-specific input limits and architecture may change the score. See [FIRST's library-scoring guidance](https://www.first.org/cvss/v3-1/user-guide). ## Reproducer The definite-length example below is valid BER. It is deliberately not a conforming CER or DER encoding. The second constructor uses conforming CER fragment sizing and framing. CPU times are machine-dependent; run the larger cases only in a local test environment. ```python import time from pyasn1.codec.ber import decoder as ber_decoder from pyasn1.codec.cer import decoder as cer_decoder from pyasn1.codec.der import decoder as der_decoder from pyasn1.error import PyAsn1Error from pyasn1.type import univ def encode_length(size): octets = size.to_bytes((size.bit_length() + 7) // 8, "big") return bytes((0x80 | len(octets),)) + octets def definite_fragments(count): body = b"\x03\x02\x00\xa5" * count # one data octet per BIT STRING fragment return b"\x23" + encode_length(len(body)) + body def cer_fragments(count): # Each primitive fragment has 1000 contents octets: one unused-bits # octet (zero) followed by 999 data octets. fragment = b"\x03" + encode_length(1000) + b"\x00" + b"\xa5" * 999 return b"\x23\x80" + fragment * cou

References

Community reviews

Loading reviews…