Exploitation - LDAP injections
Last updated
Last updated
The Lightweight Directory Access Protocol (LDAP)
is an application protocol for accessing and maintaining distributed directory information services.
Web applications may rely on LDAP to access data from a LDAP directory. If user supplied input is passed unsanitized in a LDAP statement, the statement could be altered, leading to potential authentication bypass and directory data disclosure.
LDAP filters consist of one or more criteria, such as (username=John)
, and logical AND
or OR
operators. LDAP filters follow the polish notation, with the operators placed in front of the operands (i.e. the criteria).
LDAP logical operators syntax:
Operation | Syntax | Equivalent |
---|---|---|
LDAP supports the following criteria's rules:
Rule | Syntax |
---|---|
To detect if an user supplied parameter is inserted unsanitized in a LDAP statement, the wildcard *
operator should be supplied. If a change in the application behavior can be noticed, or if a search query returns all available information, the parameter may be vulnerable to LDAP injection.
In order to properly terminate the LDAP statement, multiple closing parenthesis )
may be needed.
If a web application uses LDAP in its user authentication process, and is vulnerable to LDAP injection, the authentication mechanism could be bypassed by injecting an LDAP query that will always evaluate to true (similarly to SQL and XPATH injections).
For example, if the authentication LDAP statement is (&(username="+user+")(password="+password+")
then using *)(uid=*))(|(uid=*
as the user parameter would always evaluate to true and allow for authentication bypass.
The following payloads could be used to bypass authentication in a LDAP statement:
If no data can be directly retrieved through a LDAP statement, neither from the statement response nor from an error message, but the web application comportment varies depending on whether the statement evaluated to true or false, a blind LDAP injection could be possible.
AND / OR blind LDAP injection
LDAP injection may take place in AND
or OR
statement, each needing a different payload format:
Directory attributes brute forcing
The first step in exfiltrating data through a blind LDAP injection is to determine which are the valid attributes in the schema.
The payloads to do so are:
AND injection: *)(<ATTRIBUTE>=*))(|(<ATTRIBUTE>=we
OR injection: void)(<ATTRIBUTE>=void))(&(<ATTRIBUTE>=void
A comprehensive list of the attributes in the Active Directory, Exchange and OpenLDAP default schema is available: http://www.phpldaptools.com/reference/Default-Schema-Attributes
Note that the following Python script is only provided as an example on how to brute force attributes and should be modified accordingly to the LDAP injection exploited. In particular, the payload and the web application expected comportment should be updated.
Data retrieval
Once the targeted attribute has been identified, the booleanization technique, which consist of transforming a complex value check into a list of TRUE / FALSE statement, can be used to retrieve its value.
In LDAP, the wildcard operator can be used to conduct the booleanization technique:
Again, note that the following Python script is only provided as an example on how to brute force attributes and should be modified accordingly to the LDAP injection exploited. In particular, the payload and the web application expected comportment should be updated.
http://www.ldapexplorer.com/en/manual/109010000-ldap-filter-syntax.htm https://www.blackhat.com/presentations/bh-europe-08/Alonso-Parada/Whitepaper/bh-eu-08-alonso-parada-WP.pdf
Statement | Injection | Resulting statement | Description |
---|---|---|---|
AND
(&(CRITERIA1)(CRITERIA2))
CRITERIA1 AND CRITERIA2
OR
(|(CRITERIA1)(CRITERIA2))
CRITERIA1 OR CRITERIA2
Nested operation
(|(&(CRITERIA1)(CRITERIA2))(&(CRITERIA3)(CRITERIA4)))
(CRITERIA1 AND CRITERIA2) OR (CRITERIA3 AND CRITERIA4)
Equality
(attribute=x)
Negation
!(attribute=x)
Presence
(attribute=*)
Absence
!(attribute=*)
Greater than
(attribute>=x)
Less than
(attribute<=*)
Wildcards
(attribute=x*z)
(&(username=<INPUT1>)(password=<INPUT2>))
INPUT1 = *)(ATTRIBUTE=VALUE))(&(ATTRIBUTE=do_not_matter
(&(username=*)(ATTRIBUTE=VALUE))(&(ATTRIBUTE=do_not_matter)(password=<INPUT2>))
If the ATTRIBUTE has for value VALUE, then the statement is evaluated to True
(|(username=<INPUT1>)(email=<INPUT2>))
INPUT1 = void)(ATTRIBUTE=VALUE))(|(ATTRIBUTE=void
INPUT2 = non_existent_entry
(|(username=void)(ATTRIBUTE=VALUE))(|(ATTRIBUTE=void)(email=non_existent_entry))
If the ATTRIBUTE has for value VALUE, then the statement is evaluated to True