CVE-2026-97688 Overview
CVE-2026-97688 is an infinite loop vulnerability [CWE-835] in urllib3, a widely deployed HTTP client library for Python. The flaw affects versions from 2.6.2 through 2.7.x and is fixed in version 2.8.0. A malicious HTTP server can send a chunked response with Content-Encoding: deflate and trailing bytes after the Deflate stream. The HTTPResponse.stream and HTTPResponse.read_chunked methods then loop indefinitely, decoding the same unconsumed tail without progress. Network read timeouts cannot break the loop because no additional socket reads occur.
Critical Impact
A single malicious HTTP response can pin a Python process at 100% CPU indefinitely, causing denial of service against any client that consumes untrusted URLs with content decoding enabled.
Affected Products
- urllib3 versions 2.6.2 through 2.7.x
- Python applications using HTTPResponse.stream with a positive finite amt parameter
- Python applications using HTTPResponse.read_chunked against untrusted servers
Discovery Timeline
- 2026-09-29 - CVE-2026-97688 published to NVD
- 2026-09-29 - Last updated in NVD database
Technical Details for CVE-2026-97688
Vulnerability Analysis
The vulnerability resides in urllib3's Deflate decompression path. When a server returns a response with Transfer-Encoding: chunked and Content-Encoding: deflate, urllib3 streams the encoded body through a Deflate decoder. If the decoded body exceeds a positive finite chunk size passed as amt=N, and the encoded payload contains trailing bytes after the end-of-stream marker, the decoder retains those bytes as unconsumed input.
Each subsequent call to decompress re-processes the same trailing bytes. The Deflate object has already reached end-of-stream, so it produces no new output. However, the loop condition treats the presence of unconsumed input as work remaining. The result is an infinite loop that never issues another socket read, bypassing standard network timeouts.
Root Cause
The root cause is a missing end-of-stream check in the DeflateDecoder.decompress method in src/urllib3/response.py. Before the fix, the decoder did not verify whether the underlying zlib object had reached EOF before attempting to decompress buffered trailing bytes. Any bytes received after EOF cannot produce output, but they also do not cause the decoder to exit its work loop.
Attack Vector
An attacker controls or compromises an HTTP server that a Python client contacts. The server responds with a chunked, Deflate-encoded body whose decoded length exceeds the caller's streaming chunk size and whose encoded stream is padded with arbitrary trailing bytes. The client must have content decoding enabled and must consume the response using a positive finite amt argument to stream() or via read_chunked().
def decompress(self, data: bytes, max_length: int = -1) -> bytes:
data = self._unfed_data + data
self._unfed_data = b""
+ # Data received after EOF cannot produce more output from this
+ # Deflate stream.
+ if self._obj.eof:
+ return b""
if not data and not self._obj.unconsumed_tail:
return data
original_max_length = max_length
Source: urllib3 commit ea2ad7b. The patch short-circuits the decompression path once the Deflate stream has reached EOF, preventing the loop from re-processing trailing bytes.
Detection Methods for CVE-2026-97688
Indicators of Compromise
- Python processes consuming 100% CPU on a single core while holding an open HTTP connection to an external host
- HTTP client requests that never complete despite configured read or total timeouts
- Stack traces or py-spy samples showing repeated calls inside urllib3.response.DeflateDecoder.decompress
- Outbound connections to untrusted URLs returning Transfer-Encoding: chunked combined with Content-Encoding: deflate
Detection Strategies
- Inventory installed Python packages across build systems, containers, and runtime hosts to identify urllib3 versions between 2.6.2 and 2.7.x
- Instrument HTTP client code paths to log response headers and flag responses combining chunked transfer with deflate encoding from untrusted origins
- Add process-level CPU and wall-clock watchdogs on worker threads that perform outbound HTTP requests
Monitoring Recommendations
- Alert on sustained high CPU utilization tied to Python worker processes without corresponding network throughput
- Track long-lived TCP sessions to external hosts that exceed the application's configured request timeout by a wide margin
- Ingest Software Bill of Materials (SBOM) data into a central data lake and query for vulnerable urllib3 versions across the estate
How to Mitigate CVE-2026-97688
Immediate Actions Required
- Upgrade urllib3 to version 2.8.0 or later in all Python environments, container images, and application virtual environments
- Rebuild and redeploy container images that pin urllib3 in requirements files or lock files
- Audit any HTTP client wrappers, SDKs, and dependencies that transitively bundle urllib3
Patch Information
The fix is included in urllib3 release 2.8.0 and applied in the upstream commit. Additional context is available in GHSA-gh4c-6fx4-qh6g.
Workarounds
- Disable automatic content decoding by setting decode_content=False on stream() and read() calls, then decompress manually with bounded input
- Avoid consuming responses from untrusted servers with a positive finite amt argument until patched
- Route outbound HTTP calls through a proxy or gateway that strips Content-Encoding: deflate from responses originating from untrusted origins
# Upgrade urllib3 to the fixed release
pip install --upgrade 'urllib3>=2.8.0'
# Verify the installed version
python -c "import urllib3; print(urllib3.__version__)"
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

