LDAP injection attacks prevent

Code injection attacks are some of the most common and successful online attacks. Web applications, mobile apps, desktop programs, APIs, databases, web servers, etc., can all be vulnerable to code injection attacks if they accept user input without proper validation.

One of the most common code injection attacks is LDAP injection, and that’s what we’re going to discuss in this post. We will look at what LDAP is, how LDAP injection works, and provide tips on mitigating this attack.

What is LDAP?

LDAP stands for Lightweight Directory Access Protocol. It’s a directory service protocol used to lookup directory listings within an LDAP database, most commonly usernames and passwords. As its name implies, LDAP is very lightweight, and because of that, it scales very well and is used by a massive number of organizations today.

An LDAP directory consists of attributes based on the LDAP schema. Each entry in the schema/directory is provided with a unique identifier called a Distinguished Name (DN). Below is an example of an entry for the mock user, Johnnyny Theguy.

dn: cn=Johnnyny Theguy,dc=example,dc=com
cn: Johnnyny Theguy
givenName: Johnnyny
sn: Doe
telephoneNumber: +1 555 555 5555
telephoneNumber: +1 555 555 5555
mail: Johnnynytheguy@exampleorg.com
manager: cn=Barbara Themanager,dc=example,dc=com
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: person
objectClass: top

Many organizations use LDAP for single sign-on to provide employees access to multiple apps within the corporate network without requiring them to sign in to every single app. But beyond simply validating user credentials, LDAP is used to answer queries for information and also includes various commands used to manage the LDAP databases. And that makes sense, given the amount of information (beyond just usernames and passwords) LDAP databases contain – which also highlights the dangers of LDAP injection attacks.

LDAP queries

The LDAP queries submitted to the server are referred to as LDAP search filters and are constructed using prefix notation. A typical LDAP query usually involves the following:

  • Session connection: The user connects to the LDAP server.
  • Request: The user submits a query to the server (a user lookup, for example).
  • Response: The LDAP protocol queries the directory, locates the requested information, and provides it to the user.
  • Completion: The user disconnects from the LDAP server.

Once inside the database, users can formulate queries to execute operations over the LDAP server. Below is a list of common operations that the LDAP database/server can perform:

  • Add: Used to add new data to the database.
  • Bind (authenticate): Used for authentication and encryption.
  • Delete: Used to delete data from the database.
  • Search and Compare: The Search operation is used to search for and to read entries.
  • Modify: Used by LDAP clients to request that the LDAP server make changes to existing entries
  • Unbind: Used to close the connection.

Within its queries, LDAP supports the following metacharacters:

  •  & Boolean AND
  •  | Boolean OR
  •  ! Boolean NOT
  •  = Equals
  •  ~= Approx
  •  >= Greater than
  •  <= Less than
  •  * Any character
  •  () Grouping parentheses

Here are some examples of LDAP queries.

A typical user authentication request (i.e., a user login) would look like this:

(&(user=Johnnyny)(password=Johnnyny’sPassword))

If the credentials sent match those on the LDAP server, the user Johnny would be authenticated.

Other examples would be:

Find all users who need to change their password when they next log in:

(&(objectCategory=user)(pwdLastSet=0))

This query would return a list of all users found in the database that have the attribute pwdLastSet with the value: 0.

Find all Users with ‘pass’ or ‘pwd’ in their description

(&(objectCategory=user)(|(description=*pass*)(description=*pwd*)))

This query would return a list of all users found in the database that have the pass or pwd attributes listed.

LDAP injection

Now that we understand what LDAP is and how it works, let’s turn our attention to LDAP injection.

An LDAP injection attack can be absolutely devastating to your organization. That’s because of the quantity of high-value information an LDAP database can hold. We’re talking about names, usernames, passwords, email addresses, telephone numbers, job titles, permissions, etc.

Add to that the fact that an LDAP injection attack can mean many things. In other words, LDAP injection attacks are multi-vectored. An attacker can exploit an insecure LDAP system in many different ways. They could insert malicious code that allows them to view all the usernames and passwords contained in the database. Or they might add themselves as a user in the LDAP database with system administrator permissions. An attacker could even bypass the requirement for usernames and passwords altogether. The crux of an LDAP injection attack is to supply the server with a query that will trick the server into validating the query as true or valid.

Many factors come into play to make an LDAP injection attack succeed or fail: the attacker’s knowledge and skill level, the organization’s IT security measures, and the information contained in the LDAP database, for example. But in any event, a successful LDAP injection attack will typically be a big win for the attacker and a significant pain for the compromised organization.

Let’s look at a few examples of how this could be achieved.

Examples of LDAP injection attacks

Bypassing authentication with the ‘&’ metacharacter

Let’s take our first query example above of a user login:

(&(user=Johnny)(password=Johnny’sPassword))

On a vulnerable LDAP database, a malicious actor could bypass the authentication mechanism altogether by crafting a malicious query by inserting the metacharacter & between the user and the password attributes of the query. That would look like this:

(&(user=whatever)(&)(password=pass))

Because LDAP only parses the first two attributes, the statement becomes equivalent to:

if name = whatever & nothing = nothing

If the above query were executed on a vulnerable LDAP database/server, the result would return as true, and our malicious user “whatever” would be authenticated.

Bypassing authentication with the ‘*’ and ‘|’ metacharacters

Let’s use a similar example as above (“cn” stands for common name):

(&(cn=Johnny)(password=Johnny’sPassword))

We can achieve the same as above using the * and | metacharacters. If we set the username value to *)(cn=*))(|(cn=*, the search filter becomes:

(&(cn=*)(cn=*))(|(cn=*)(password=pass))

Because LDAP only parses the first two attributes, the above query is always returned as true. This query would enable an attacker to bypass LDAP’s authentication mechanism without proper input validation.

Listing all users in the database with ‘*’

The query below is an LDAP search query.

find(“(&(cn=Johnny)(password=Johnny’sPassword))")

The above prefix filter notation instructs the query to find an LDAP node with the given username and password. However, if the LDAP database is vulnerable to LDAP injection, an attacker could substitute the cn and password in the above example with *, like so:

find(“(&(cn=*)(password=*))”)

That would change the query’s intended meaning, and the database would return a list of all users.

The risks associated with LDAP injection

The harms that can occur from LDAP injection attacks are similar to other injection attacks. To inject code into a server implies the ability to obtain information and modify information. Hence, LDAP injection attacks can lead to the following:

Leaking sensitive data

As we saw with our last example, it is possible to manipulate LDAP queries sent to a vulnerable server to list unintended information. In our example above, we showed how a maliciously crafted query could lead the database to output the list of all the users in the database.

However, if the server is vulnerable to LDAP injection, it could be manipulated to output other sensitive data. LDAP databases tend to store more data than simply usernames and passwords – which would already be damaging enough if leaked. Because of that, an attacker could craft LDAP queries to obtain sensitive information like email addresses, telephone numbers, and even social security numbers. So on top of compromised accounts and unauthorized access to company resources (which can be devastating), LDAP injection could also lead to identity theft, targeted phishing campaigns, etc. Nasty.

Denial of service attacks

LDAP injection attacks can also lead to straightforward and very effective denial of service (DoS) attacks. This attack can be mounted against the application that interacts with the directory server or against the directory server itself. If an attacker can craft enough malicious LDAP queries that are highly time-consuming and resource-hungry, it could consume all of the available resources so that other requests can’t get through.

Furthermore, suppose the application was designed to hold all entries returned from a search query in memory. In that case, a query intended to return many more entries than expected could lead to the application consuming all of its available memory to process that request. The result of that would be that the application crashes.

Altered files and data corruption

We saw how a maliciously crafted query could expose unintended data to the attacker. But it’s also possible to trick the database into updating unintended files, either with junk, to corrupt the file or by editing the passwords to one set by the attacker. If the attacker can trick the application into searching for the wrong entries, they can trick it into updating incorrect entries, which could cause data loss or corruption.

How to prevent LDAP injection attacks

Now that you know the threats, here are some precautions you can take to prevent LDAP injection attacks.

Enforce input validation

In other words: don’t trust user input. Regardless of the type of user (authenticated, internal, or public), consider that input untrusted. If possible, bar your applications from copying user-controllable data into LDAP queries. If that’s not feasible, make sure to validate user input against a list of allowed strings or characters. And this validation should always occur server-side even if the input was previously validated on the client-side.

You can use a strong regular expression pattern to validate structured inputs like Social Security numbers, phone numbers, and email addresses. Inputs such as usernames should be validated against an approved set of characters that exclude LDAP metacharacters. The characters that should be blocked include ( ) ; , * | & = and whitespace

Escape input with encoding

Escape user-controlled input strings so that any control characters in the input don’t change the intended meaning of the LDAP search filter. For example, in a Java application, the metacharacters of an LDAP query can be entered with backslashes as escape characters. This way, untrusted inputs are appended to a search query as literal string values, not as LDAP predicates.

It’s also strongly recommended to use existing libraries for escaping – don’t write your own, as you risk introducing unintended vulnerabilities.

Implement the principle of least privilege

The principle of least privilege is an IT security policy that states that one should only assign the minimum necessary rights to a user that requires access to a resource. Those rights should also be in effect for the shortest possible duration. Specifically, the LDAP account used for binding the directory in an application should have restricted access. Only authorized LDAP queries should be executed against the LDAP server.

Wrap up

So those are the ins and outs of LDAP injection attacks. They’re pretty nasty, and, like many (if not most) online attacks, their consequences could be catastrophic. As is often the case with these kinds of attacks, sanitizing user input is the most crucial mitigation measure here. Not sanitizing your users’ input is like having the front door to your house unlocked – anyone can simply turn the knob and come in. That’s not what you want with a web application/server. Hopefully, the tips outlined above will help you to avoid that scenario.

As always, stay safe.