VAITP Dataset

← Back to the dataset

CVE-2025-61912

python-ldap: Incorrect NUL byte escaping leads to a client-side DoS.

  • CVSS 5.5
  • CWE-116
  • Input Validation and Sanitization
  • Remote

python-ldap is a lightweight directory access protocol (LDAP) client API for Python. In versions prior to 3.4.5, ldap.dn.escape_dn_chars() escapes \x00 incorrectly by emitting a backslash followed by a literal NUL byte instead of the RFC-4514 hex form \00. Any application that uses this helper to construct DNs from untrusted input can be made to consistently fail before a request is sent to the LDAP server (e.g., AD), resulting in a client-side denial of service. Version 3.4.5 contains a patch for the issue.

CVSS base score
5.5
Published
2025-10-10
OWASP
A03 Injection
Orthogonal defect classification
Function
Code defect classification
Incorrect Algorithm
Category
Input Validation and Sanitization
Subcategory
Insecure Parsing or Deserialization
Accessibility scope
Remote
Impact
Denial of Service (DoS)
Affected component
python-ldap
Fixed by upgrading
Yes

Solution

Upgrade python-ldap to version 3.4.5 or later.

Vulnerable code sample

import ldap
import ldap.dn

# This code must be run with a vulnerable version of python-ldap (e.g., < 3.4.4)
# pip install python-ldap==3.4.3

# 1. Define an untrusted input string containing a NUL byte (\x00).
untrusted_username = "malicious\x00user"

# 2. Use the vulnerable function ldap.dn.escape_dn_chars().
# In vulnerable versions, this incorrectly escapes '\x00' as a backslash
# followed by a literal NUL byte, instead of the string r'\00'.
escaped_rdn_component = ldap.dn.escape_dn_chars(untrusted_username)

# 3. Construct a full Distinguished Name (DN) using the malformed output.
# The resulting string now contains a literal NUL byte.
malformed_dn = f"cn={escaped_rdn_component},ou=users,dc=example,dc=com"

# 4. Attempt to use the malformed DN in any LDAP operation.
# The LDAP client library will fail to parse this string before it even
# attempts to send a request to the server.
try:
    # Initializing a connection is sufficient. The error is client-side.
    conn = ldap.initialize("ldap://localhost")
    
    # This call will raise ldap.INVALID_DN_SYNTAX because the C library
    # backing python-ldap cannot handle a DN string with a NUL byte in it.
    conn.search_s(malformed_dn, ldap.SCOPE_BASE)
    
except ldap.INVALID_DN_SYNTAX as e:
    # This exception demonstrates the client-side Denial of Service.
    # An application processing untrusted input would crash here.
    print(f"Vulnerability Triggered: Caught expected exception.")
    print(f"Error Type: {type(e)}")
    print(f"Error Details: {e}")
except Exception as e:
    print(f"An unexpected exception occurred: {e}")

Patched code sample

import re

def escape_dn_chars_vulnerable(value):
    """
    Represents the vulnerable behavior in python-ldap < 3.4.5.
    This function incorrectly escapes the NUL byte ('\x00').
    """
    # Simplified escaping for other common characters for context.
    # The actual library implementation is more comprehensive.
    value = re.sub(r'([,\\+<>;\"=#])', r'\\\1', value)

    # CVE-2025-61912: Incorrectly escapes NUL as a backslash
    # followed by a literal NUL byte. This can cause C-based
    # string parsers to fail, leading to a client-side DoS.
    return value.replace('\x00', '\\\x00')

def escape_dn_chars_fixed(value):
    """
    Represents the fixed behavior in python-ldap >= 3.4.5.
    This function correctly escapes the NUL byte per RFC-4514.
    """
    # Simplified escaping for other common characters for context.
    value = re.sub(r'([,\\+<>;\"=#])', r'\\\1', value)

    # THE FIX: Correctly escape NUL as a backslash followed by "00".
    # This produces a valid string that can be safely processed.
    return value.replace('\x00', '\\00')

# --- Demonstration ---

# An example of untrusted input containing a NUL byte.
untrusted_input = "cn=test\x00user,ou=people"

# 1. How the vulnerable version processed the input.
# The repr() output shows the problematic literal '\x00' remains.
vulnerable_output = escape_dn_chars_vulnerable(untrusted_input)

# 2. How the fixed version processes the input.
# The repr() output shows the correct RFC-4514 escaping '\\00'.
fixed_output = escape_dn_chars_fixed(untrusted_input)


# Print results to show the difference.
print("--- Demonstration of the fix for CVE-2025-61912 ---")
print(f"Original Untrusted Input: {untrusted_input!r}")
print("-" * 55)
print("Vulnerable Output (< 3.4.5):")
print(f"  - Result: {vulnerable_output!r}")
print(f"  - Contains literal NUL byte: {'\\x00' in vulnerable_output}")
print("\nFixed Output (>= 3.4.5):")
print(f"  - Result: {fixed_output!r}")
print(f"  - Contains literal NUL byte: {'\\x00' in fixed_output}")

Payload

"cn=some_user\x00_injected"

Cite this entry

@misc{vaitp:cve202561912,
  title        = {{python-ldap: Incorrect NUL byte escaping leads to a client-side DoS.}},
  author       = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
  year         = {2025},
  note         = {VAITP Python Vulnerability Dataset, entry CVE-2025-61912},
  howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2025-61912/}}
}
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 ::