Every API that routes login traffic through Active Directory or OpenLDAP builds a filter string at runtime. That filter, assembled from user input, reaches the directory server unchanged. There is no parameterization layer, no prepared-statement equivalent. The LDAP server executes whatever filter it receives.
LDAP injection at POST /api/auth grants authentication without valid credentials because the filter executes before the middleware validates its own integrity. The LDAP server returns a match. The application never asked whether the filter was tampered with.
The Login Filter Is a String, Not a Query Object
API authentication against LDAP builds filter strings through concatenation. The standard auth filter is (&(uid={username})(userPassword={password})). The {username} field comes directly from the request body, with no intermediate processing.
No major LDAP library enforces safe filter construction by default. Spring LDAP provides LdapUtils.escapeLDAPFilter(), but LdapTemplate.search() methods do not call it automatically. The developer must call the escape function explicitly before building the filter, at every construction point in the code.
ldapjs for Node.js accepts raw filter strings without any escaping. The Filter object API exists and is safe, but rarely appears in production code authenticating against enterprise LDAP. python-ldap has ldap.filter.escape_filter_chars() with escape_mode=0 as the safe default. Setting escape_mode=1 and passing a crafted list as the assertion_value parameter creates a bypass condition documented in published research.
The gap is not in the concept of escaping. Each of these libraries has the right tool available. The gap is that the path of least resistance, in each library, leads to unsafe concatenation.
Filter Corruption Bypasses Authentication Without a Valid Password
Supplying *)(uid=*))(|(uid=* as the username completely rewrites the filter semantics. The LDAP server receives:
(&(uid=*)(uid=*))(|(uid=*)(userPassword=anything))
The first clause (uid=*) matches every directory entry. LDAP returns the first matching entry. The middleware receives a valid search response and grants the authenticated session. The attacker-supplied password is not validated at any point in this flow.
Null byte injection (\00) terminates filter string parsing in some LDAP library implementations. The effect is to truncate the filter before the password field. The password field becomes completely irrelevant to the authentication decision.
CVE-2022-0730 documents this pattern in the Cacti monitoring platform. The LDAP authentication bypass received CVSS 9.8, with no authentication required and network-accessible. The full CVSS vector is AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H: any attacker with network access exploits the vulnerability without any prior privilege.
5 CVEs with CVSS 7.5+ Confirm the Pattern Across Frameworks and Products
LDAP injection in API authentication is not a theoretical concern. There are CVSS 9.8 and 9.2 instances in shipping products across 2021 to 2024, with one npm package that remains unpatched.
| CVE | Product | Mechanism | CVSS |
|---|---|---|---|
| CVE-2022-0730 | Cacti | LDAP auth bypass with certain credential types | 9.8 |
| CVE-2024-10127 | M-Files Server (before 24.11) | Auth bypass without password when anonymous binding enabled | 9.2 |
| CVE-2024-37393 | SecurEnvoy MFA (before 9.4.514) | Blind LDAP injection via /secserver, exfiltrates AD attributes |
7.5 |
| CVE-2021-23335 | npm is-user-valid
|
All versions vulnerable; no fixed version available | 7.5 |
| CVE-2023-4727 | dogtag-pki / pki-core |
sessionID=* bypasses token authentication via LDAP wildcard |
6.5 (Medium) |
CVE-2024-10127 in M-Files is notable for its required condition: anonymous binding enabled on the LDAP server. This configuration is common in environments where the application must search directory entries before authenticating users. CVE-2024-37393 in SecurEnvoy MFA carries an EPSS score of 82.3%, indicating high observed exploitation probability.
CVE-2021-23335 in the is-user-valid package has no fix available. The library was abandoned with all versions in a vulnerable state. CVE-2023-4727 in dogtag-pki shows the pattern is not limited to password fields. The sessionID=* query parameter bypasses token authentication via LDAP wildcard and escalates the attacker's privilege.
Blind Injection Maps Directory Structure When Direct Bypass Is Blocked
Length and character restrictions do not eliminate the risk. Boolean-based blind injection still extracts usernames, group memberships, and attribute values character by character.
The filter (uid=a*) returns success: the application responds with login success. The filter (uid=b*) fails: the application responds with an error. Sequential enumeration via boolean true/false responses reveals all valid usernames in the directory without any authentication.
Extracting the memberOf attribute uses the pattern (memberOf=CN=Domain Admin*). A true result confirms the tested user belongs to the Domain Admins group. The Black Hat EU 2008 whitepaper (Alonso and Parada) documents the charset reduction technique. The attacker progressively narrows the search space until obtaining the complete value of each attribute through booleanization.
HackerOne #359290 records this pattern in a U.S. Department of Defense endpoint. The LDAP injection allowed directory traversal through injected filters in production. HackerOne #956295 from GitHub Security Lab identified the same pattern during code-level analysis, confirming the vulnerability exists without evidence of active exploitation.
Enterprise AD Scope Turns a Login Bypass Into a Directory Intelligence Operation
B2B APIs and enterprise SSO endpoints authenticating against Active Directory turn LDAP injection from a credential bypass into full directory enumeration.
The API's bindDN account, used to search the directory before bind, has read access to most AD attributes by default. Attributes accessible through blind injection include mail, telephoneNumber, memberOf, userAccountControl, pwdLastSet, and adminCount. Extracting memberOf identifies Domain Admins and Enterprise Admins before any lateral movement.
HackerOne #1004412 documents LDAP credential disclosure at Acronis: bindDN credentials including base DN, admin user, and admin password exposed in a public GitHub repository, confirming that bindDN accounts are high-value exfiltration targets once injection provides directory read access. LDAP over TLS (LDAPS, port 636) prevents interception in transit but does not prevent injection. The malformed filter is built inside the application before reaching the TLS layer.
RFC 4515 Escaping Closes the Injection Path
Explicit escaping per RFC 4515 before filter construction is the only reliable defense. The CVEs above exist because developers trusted framework abstractions that either do not escape automatically or that can be bypassed with specific configurations.
The RFC 4515 escape map:
| Character | Escape sequence |
|---|---|
* |
\2a |
( |
\28 |
) |
\29 |
\\ |
\5c |
NUL |
\00 |
OWASP ESAPI provides encodeForLDAP() for search filters and encodeForDN() for distinguished names. .NET AntiXSS offers Encoder.LdapFilterEncode() and Encoder.LdapDistinguishedNameEncode(). The two functions cover distinct contexts: search filters and distinguished names have different sets of dangerous characters.
The allowlist approach restricts username input to the pattern [a-zA-Z0-9._@-] before filter construction. Inputs containing LDAP metacharacters are rejected at the API boundary before reaching the construction code. The two defenses are complementary: the allowlist blocks most trivial payloads, and RFC 4515 escaping covers the remaining cases.
In python-ldap, the safe pattern is ldap.filter.escape_filter_chars() with escape_mode=0. Setting escape_mode=1 with untrusted list input recreates the bypass condition, even with the escape call present in the code.
WAF rules that pattern-match on LDAP metacharacters (*, (, ), \) provide a detection layer but not a prevention layer. Blind injection produces valid application responses (the LDAP server returns a result, the application serves a 200) that WAF pattern-matching on request content cannot distinguish from legitimate traffic.
The MAGO Intel tool (intel.mago.team) probes login endpoints with LDAP metacharacter payloads (*, (, ), \\, \00). The scan is automated and runs against the live API surface, without access to the source code.
The vulnerability lives in the code that constructs the filter. Fix the construction, not the server.
Top comments (0)