What is LDAP?
Lightweight Directory Access Protocol (LDAP) is a protocol that allows the exchange of information using the TCP/IP protocol. It is intended for the use of directory services; i.e., object-oriented databases representing network users and resources. LDAP is widely used in many services of which Microsoft’s Active Directory is probably most known.
LDAP is based on the client-server model. One or more LDAP servers that have data grouped them into a so-called “LDAP directory tree”. The LDAP client connects to the server and sends the request. The server responds with a resource or points to the client where they can find more information (another LDAP server). No matter what server the client connects to, they always see the same directory structure.
Information in catalogs is stored in the form of entries. Each entry is an object of one or more classes, and each class consists of one or many attributes.
The entries are organized in the form of the structure of the already mentioned “directory tree”. They are uniquely identified by the DN (Distinguished Name); attributes, about which we can think of as a path to the file (e.g., /home/Desktop/myfile.txt).
LDAP Injection is an attack similar in form to an SQL Injection attack, so the same exploitation techniques can be used similarly. LDAP Injection is to be used in a web application constructing an LDAP expression, the possibility of entering data by the user, to unauthorized obtaining data from the database, modifying or increasing the rights. The attack is exposed to applications that do not filter data appropriately, and a deeper understanding will be achieved after analyzing the following examples.
In this example, we’ll see how by using LDAP Injection, we can bypass the authentication mechanism. Let’s take a few moments first to understand the syntax of the LDAP query. From the database we retrieve the user records by its CN (Common Name) attribute, which is one of the required attributes of the “person” class. For this purpose, we will use the query (cn = [NAME]). To “glue” the next condition, we must use logical operators OR “|” or AND “&”. LDAP queries are constructed in the so-called Polish notation, which means that they have a logical operator at the beginning. The query retrieving records from the database whose user name is equal to Marek or Jarek looks like this:
To check whether the given name Marek and the corresponding password marek123 are correct, we will use the following query:
LDAP also uses a wildcard character ( * ) that finds all possible values. We can maneuver it to receive a string that meets our requirements. E.g.:
(cn=Mar*) finds all records that cn begins with “Mar”.
(cn=*are*) will find all records that cn has “are” in.
Once we know the basics, we can take care of our web application connected to the LDAP server. The application accepts the credentials from the user, constructs the query and sends them to the server.
If the data is not filtered, would it be enough if we enter * in the “login” and “password” fields? It will not be so easy, because although LDAP supports keeping passwords in plain text, we can guess that no one is keeping them in that format any more. So we have only one field available and we can suspect that the query sent to the server looks like this:
So let’s try to maneuver the login parameter. The query starts with the AND operator, so to be correct we must provide it with at least two conditions. We want to give him two conditions that are always true. We will start with the ending of this already started using *), and then we will insert the second—absolute true. It could be identical to the previous one; i.e., (cn=*), but for the sake of insertion we will insert the symbol (&)—absolute truth. Now we will close the first fragment of the query “)”, and on the other we will make a separate data filter (| (cn = *.) The LDAP server will interpret the following query:
Instead of one, now we have two filters: (&(cn=*)(&)) and (|(cn=*)(userPassword=MD5[haslo]))
In fact, it does not matter what is in the second fragment, as it will be ignored, and the server interpreting only (&(cn=*)(&)) positively authenticates us.
However, we do not always observe such behavior, because some LDAP services, including Microsoft ADAM (Active Directory Application Mode) do not accept queries with two filters. Therefore, immediately after completing the first one, we will place NULL BYTE (%00 in URL encoding).
Thus, we get rid of the second filter and we are authenticated.
This time, our web application lists the student’s first and last name corresponding to the index we have entered. The application does not filter the data entered by the user, so we can guess on the basis of returned responses (true or false) to which we should not have access. This type of attack is called “blind” (Blind LDAP Injection).
The query sent by the application looks like:
However, we know that in addition to the name and surname, the student’s phone number is also in the database and we will try to “guess” that number. To do this, create a third condition for the AND (&) operator by entering index number 2766810)(telephonenumber=*.
Query “(& (index = 2766810) (telephonenumber = *) (ou = Students))” does not return an error (it returns exactly the same data), so we know that the “telephonenumber” attribute is correct, and now we will try to extract the value of this attribute using a mechanism called “booleanization.”
Based on the answers, we will progressively learn the student’s number. We check first if it does not start with the number 1.
When the application returns us an error, we know that the condition (telephonenumber = 1 *) is not met and we try to continue with the next digits. Finally, when entering 2766810) (telephonenumber=5* and we get the correct answer, it means that the first digit of the student’s phone number is 5.
Now, we iterate the second digit until the correct answer is received: 2766810) (telephonenumber = 50 *, etc.) We can guess in various ways; e.g., by narrowing down the character set: 2766810) (telephonenumber=*3*, we check if 3 is in phone number. “Manually” guessing the numbers of all students would be time-consuming, but we can easily automate this process.
The basic protection against LDAP injection is the validation of data entered by the user. They must be cleared of all characters that could be used to initiate the attack. It’s best to follow the whitelist rule and filter data based on regular expression, allowing only individual characters to be entered.
An additional line of defense is the validation of data returned to the user and the limitation of their number in a single response.
To minimize potential damages, it is worth applying the Least Privilege principle; i.e., to allocate the web application with the smallest rights that are necessary.
The LDAP administrator should control access to resources. When determining permissions for individual objects, it should consider which ones can be modified and which ones the user cannot access. This is all the more important when the service uses an increasingly common solution—single sign-on.
LDAP injection is an attack that uses LDAP expressions in web applications, the ability to enter data by the user. Its disadvantage compared to SQL Injection is that, due to the syntax, malicious code is injected after defining the logical operator. LDAP injection allows you to bypass authorization and authentication mechanisms. The attacker can also get sensitive data, but it is more difficult to implement.