Skip to main content
CVE Vulnerability Database

CVE-2026-9375: urllib3 Decompression Bomb DoS Vulnerability

CVE-2026-9375 is a decompression bomb bypass denial of service vulnerability in urllib3 2.6.3 affecting Brotli streaming. Attackers can trigger out-of-memory conditions. This article covers technical details, impact, and mitigations.

Published:

CVE-2026-9375 Overview

CVE-2026-9375 is a denial-of-service vulnerability in urllib3 version 2.6.3 when paired with Brotli 1.2.0. The flaw bypasses the max_length decompression-bomb safeguard introduced in urllib3 2.6.0 to mitigate CVE-2025-66471. Three independent code paths in response.py permit unbounded decompression when streaming with preload_content=False. A malicious HTTP server can return a small compressed payload that expands to consume all available memory. Applications and libraries using requests or urllib3 to stream content from untrusted sources are affected [CWE-400].

Critical Impact

A remote, unauthenticated attacker controlling an HTTP response can trigger an out-of-memory condition in any client streaming Brotli-compressed content, producing a denial of service.

Affected Products

  • urllib3 version 2.6.3
  • Brotli 1.2.0
  • Applications using requests or urllib3 streaming APIs with Brotli decoding

Discovery Timeline

  • 2026-06-19 - CVE-2026-9375 published to the National Vulnerability Database
  • 2026-06-22 - Last updated in NVD database

Technical Details for CVE-2026-9375

Vulnerability Analysis

The issue is a decompression-bomb safeguard bypass in the urllib3 streaming API. Version 2.6.0 added a max_length argument to limit how many bytes a decoder produces per read. CVE-2026-9375 identifies three paths in src/urllib3/response.py that fail to honor that limit when Brotli decompression is active. The result is unbounded memory growth from attacker-controlled compressed responses, exhausting the host and denying service to the application.

Root Cause

Three independent defects defeat the limit. First, buffer arithmetic in read() can yield negative max_length values, which the Brotli decoder treats as unbounded. Second, flush_decoder unconditionally overrides max_length to -1, removing the cap during flush operations. Third, _flush_decoder() passes no limit at all, defaulting to unlimited decompression. The official Brotli library does not enforce an internal output ceiling, so the missing argument is sufficient to produce arbitrary expansion ratios.

Attack Vector

An attacker hosts or controls an HTTP endpoint and serves a response with Content-Encoding: br containing a highly compressible payload such as a long run of repeated bytes. When a victim client issues a streaming request — requests.get(url, stream=True) or urllib3 with preload_content=False — the second read(amt=N) call, or a drain_conn() issued after a partial read, routes through one of the unbounded paths. The decoder expands the small payload into memory until the process is killed.

python
# Security patch in src/urllib3/response.py
# Source: https://github.com/urllib3/urllib3/commit/2bdcc44d1e163fb5cc48a8662425e35e15adfe6a
         Unread data in the HTTPResponse connection blocks the connection from being released back to the pool.
         """
         try:
-            self.read(
-                # Do not spend resources decoding the content unless
-                # decoding has already been initiated.
-                decode_content=self._has_decoded_content,
-            )
+            self._raw_read()
         except (HTTPError, OSError, BaseSSLError, HTTPException):
             pass
+        if self._has_decoded_content:
+            # `_raw_read` skips decompression, so we should clean up the
+            # decoder to avoid keeping unnecessary data in memory.
+            self._decoded_buffer = BytesQueueBuffer()
+            self._decoder = None

The patch replaces the decoding read in drain_conn() with _raw_read() and explicitly discards the decoder state, eliminating the unbounded decompression path. See the GitHub commit for the full diff.

Detection Methods for CVE-2026-9375

Indicators of Compromise

  • HTTP responses carrying Content-Encoding: br with a small Content-Length followed by sustained client memory growth.
  • Python processes consuming requests or urllib3 that terminate with MemoryError or are killed by the OOM killer shortly after a streaming HTTP call.
  • Outbound HTTP traffic from application servers to low-reputation hosts immediately preceding the OOM event.

Detection Strategies

  • Inventory Python dependencies and flag any host running urllib3==2.6.3 with Brotli==1.2.0 installed in the same environment.
  • Use software composition analysis to identify transitive dependencies on the vulnerable urllib3 release through requests, httpx[brotli], and similar packages.
  • Correlate process memory spikes with recent HTTPS connections to untrusted endpoints in host telemetry.

Monitoring Recommendations

  • Alert on Python worker processes exceeding configured RSS thresholds and on repeated oom-kill events in kernel logs.
  • Monitor for Brotli-encoded responses with compression ratios above an operational baseline (for example, ratios exceeding 1000:1).
  • Track egress connections from server-side HTTP fetchers (proxies, link previewers, web scrapers) for anomalous response sizes after decompression.

How to Mitigate CVE-2026-9375

Immediate Actions Required

  • Upgrade urllib3 to a release that includes the fix from commit 2bdcc44d1e163fb5cc48a8662425e35e15adfe6a referenced in advisory GHSA-mf9v-mfxr-j63j.
  • Audit every service that issues outbound HTTP requests to untrusted destinations and confirm the dependency upgrade has been deployed.
  • Rebuild and redeploy container images so cached layers do not reintroduce urllib3==2.6.3.

Patch Information

The fix is delivered in the urllib3 security commit 2bdcc44, tracked under advisory GHSA-mf9v-mfxr-j63j. The changelog documents two high-severity decompression-bomb bypasses: one triggered by HTTPResponse.drain_conn() after a partial read, and one triggered on the second HTTPResponse.read(amt=N) or HTTPResponse.stream(amt=N) call when the response was decoded with the official Brotli library. Additional context is available in the Huntr report.

Workarounds

  • Uninstall the Brotli package where it is not strictly required, forcing urllib3 to fall back to non-Brotli content encodings.
  • Strip or refuse Content-Encoding: br responses at an egress proxy until clients are patched.
  • Wrap streaming reads in a memory-bounded supervisor (for example, a subprocess with RLIMIT_AS) so a runaway decoder cannot exhaust the host.
bash
# Configuration example: remove Brotli support until urllib3 is patched
pip uninstall -y Brotli brotlicffi
pip install --upgrade 'urllib3>2.6.3'
pip show urllib3 | grep -i version

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.