The vulnerability lies in the unbounded decompression of JWE tokens that use the zip=DEF (DEFLATE) compression algorithm. The analysis of the provided patch (commit e0863d5129316b1790eee5f14cece32a03b8184d) pinpoints the exact location of the vulnerability.
The root cause is in the DeflateZipAlgorithm.decompress method within authlib/jose/rfc7518/jwe_zips.py. The original code called zlib.decompress(s, -zlib.MAX_WBITS) without imposing a limit on the size of the decompressed output. This allows a malicious actor to send a small, specially crafted JWE token that expands into a massive amount of data upon decryption and decompression, thereby exhausting system resources (CPU and memory) and causing a denial of service.
The patch addresses this by replacing the direct call to zlib.decompress with a safer mechanism using zlib.decompressobj(). It introduces a MAX_SIZE constant (set to 250 KB) and checks if the decompressed output exceeds this limit, throwing a ValueError if it does. This prevents the decompression bomb from consuming unbounded resources.
Therefore, the function DeflateZipAlgorithm.decompress is the direct vulnerable function that would appear in a runtime profile during an exploit, as it is the function performing the unsafe decompression operation.
DeflateZipAlgorithm.decompressauthlib/jose/rfc7518/jwe_zips.py
| Package Name | Ecosystem | Vulnerable Versions | First Patched Version |
|---|---|---|---|
| authlib | pip | < 1.6.5 | 1.6.5 |
python3 -m venv .venv
.venv/bin/pip install --upgrade pip
.venv/bin/pip install -e .
.venv/bin/python /authlib/jwe_deflate_dos_demo.py --size 50 --max-rss-mb 2048
Sample output (abridged):
LOCAL TEST ONLY – do not send to third-party systems.
Runtime: Python 3.13.6 / Authlib 1.6.4 / zip=DEF via A256GCM
[CASE] normal plaintext=13B ciphertext=117B decompressed=13B wall_s=0.000 cpu_s=0.000 peak_rss_mb=31.0 ratio=0.1
[CASE] malicious plaintext=50MB ciphertext=~4KB decompressed=50MB wall_s=~2.3 cpu_s=~2.2 peak_rss_mb=800+ ratio=12500+
The second case shows the decompression spike: a few KB of ciphertext forces allocation and processing of ~50 MB during decrypt. Repeated requests can quickly exhaust available memory and CPU.
Reproduction notes:
alg=dir, enc=A256GCM, header includes { "zip": "DEF" }."A" * N).--size to stress memory; the --max-rss-mb flag helps avoid destabilizing the host during testing.zip=DEF tokens.zip=DEF and where an attacker can submit tokens that will be successfully decrypted (e.g., shared dir key, token reflection, or compromised/abused issuers).Base vector (typical shared‑secret scenario where the attacker must produce a decryptable token):
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:N/A:H → 6.5 (MEDIUM)Rationale:
alg=dir and shared keys across services.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H → 7.5 (HIGH)zip=DEF for inbound JWEs at the application boundary until a fix is available.zlib.decompress(..., max_length) via decompressobj().decompress(data, MAX_SIZE)), returning an error when output exceeds a safe limit.joserfc’s approach: add a conservative maximum output size (e.g., 256 KB by default) and raise a specific error when exceeded; document a controlled way to raise this ceiling for trusted environments.authlib/authlib/jose/rfc7518/jwe_zips.py, authlib/authlib/jose/rfc7516/jwe.pyOngoing coverage of React2Shell