What is LDAP Injection?
Many companies use LDAP services. LDAP serves as a repository for user authentication, and also enables a single sign-on (SSO) environment. LDAP is most commonly used for privilege management, resource management, and access control.
LDAP Injection attacks are similar to SQL Injection attacks. These attacks abuse the parameters used in an LDAP query. In most cases, the application does not filter parameters correctly. This could lead to a vulnerable environment in which the hacker can inject malicious code.
LDAP exploits can result in exposure and theft of sensitive data. Advanced LDAP Injection techniques can also execute arbitrary commands. This lets them obtain unauthorized permissions and also alter LDAP tree information.
Environments that are most vulnerable to LDAP Injection attacks include ADAM and OpenLDAP.
In this article, you will learn:
- What is LDAP Injection?
- How Do LDAP Injection Attacks Work?
- Types of LDAP Injections
- LDAP Injection Examples Using Logical Operators
- BLIND LDAP Injections
- How to Prevent LDAP Vulnerabilities
How Do LDAP Injection Attacks Work?
Clients query an LDAP server by sending a request for a directory entry that matches a specific filter. If an entry matching the LDAP search filter is found, the server returns the requested information.
Search filters used in LDAP queries follow the syntax specified in RFC 4515. Filters are constructed based on one or more LDAP attributes specified as key/value pairs in parentheses. Filters can be combined using logical and comparison operators and can contain wildcards.
Here are some examples:
(cn=David*)
matches anything with a common name beginning with the stringDavid
(the asterisk matches any character).(!(cn=David*))
matches anything where the common name does not start with the stringDavid
.(&(cn=D*)(cn=*Smith))
uses the AND logical operator, represented by the&
symbol. Matches entries that start with the letter D and end with Smith.(|(cn=David*)(cn=Elisa*))
uses the OR logical operator, represented by the pipe symbol. Matches entries whose common name starts with one of the strings Dave or Elisa.
Similar to SQL injection and related code injection attacks, an LDAP injection vulnerability results when an application injects unfiltered user input directly into an LDAP statement. An attacker can use LDAP filter syntax to pass a string value, which will cause the LDAP server to execute various queries and other LDAP statements. Typically the injected command will exploit misconfiguration or inappropriate permissions set on the LDAP server.
Types of LDAP Injection Attacks
Access Control Bypass
All login pages have two text box fields. One for the username, one for the password. The user inputs are USER(Uname) and PASSWORD(Pwd). A client supplies the user/password pair. To confirm the existence of this pair, LDAP constructs search filters and sends them to the LDAP server.
(&(USER=Uname)(PASSWORD=Pwd))
An attacker can enter a valid username (john90 for example) while also injecting the correct sequence after the name. This way they successfully bypass the password check. By knowing the username any string can be introduced as the Pwd value. Then the following query gets sent to the server:
(&(USER=john90)(&))(PASSWORD=Pwd))
The LDAP server processes only the first filter. The query processes only the (&(USER=john90)(&) query. Since this query is always correct, the attacker enters the system without the proper password.
Elevation of Privileges
Some queries list all documents and they’re visible to users that have a low-security level. As an example /Information/Reports, /Information/UpcomingProjects, etc. files in the directory. The “Information” part is the user entry for the first parameter. All these documents have a “Low” security level. The “Low” part is the value for the second parameter. This also allows the hacker to access high-security levels. In order to do that the hacker must use an injection that looks something like this:
“Information)(security_level=*))(&(directory=documents”
This injection results in this filter:
(&(directory=Information)(security_level=*))(&(directory=Information)(security_level=low))
If you’ve been paying attention you know the LDAP processes the first filter. The second filter gets ignored. The query that gets processed is (&(directory=Information)security level=*)). The (&(directory=Information)(security level=low)) is ignored completely. That’s how hackers see a list of documents that can usually only be accessed by users with all security levels. Even though the hacker doesn’t actually have privileges to see this information.
Information Disclosure
Some resource explorers let a user know exactly which resource is available in the system. For example, a website dedicated to selling clothing. The user can look for a specific shirt or pants and see if they are available for sale. In this situation OR LDAP Injections are used:
(|(type=Resource1)(type=Resource2))
Both Resource1 and Resource2 show the kinds of resources in the system. Resource1=Jeans and Resource2=T-Shirts show all the jeans and T-Shirts that are available for purchase in the system. How do hackers exploit this? By injecting (uid=*) into Resource1=Jeans. This query then gets sent to the server:
(|(type=Jeans)(uid=*))(type=T-Shirts))
The LDAP server then shows all the jeans and user objects.
LDAP Injection Examples Using Logical Operators
An LDAP filter can be used to make a query that’s missing a logic operator (OR and AND). An injection like:
“value)(injected_filter”
Results in two filters (the second gets ignored while the first one gets executed in OpenLDAP implementations):
(attribute=value)(injected_filter)
ADAM LDAP doesn’t allow queries with two filters. This renders this injection useless. Then we have the & and | standalone symbols. Making queries with them looks something like this:
(&(attribute=value)(second_filter))
(|(attribute=value)(second_filter))
Filters that have the OR or AND logic operators can make queries in which this injection:
“value)(injected_filter”
Results in this filter:
(&(attribute=value)(injected_filter)) (second_filter)).
As you can see, this filter isn’t even syntactically correct. Yet, OpenLDAP will process it regardless. It will go from left to right and ignore all characters after the first filter closes. What does that entail? Certain LDAP Client components ignore the second filter. The first complete one is sent to ADAM and OpenLDAP. That’s how injections bypass security.
In cases where applications have a framework that checks the filter, it needs to be correct. An example of a synthetically correct injection looks something like:
“value)(injected_filter))(&(1=0”
This shows two different filters where the second one gets ignored:
(&(attribute=value)(injected_filter))(&1=0) (second_filter)).
Since certain LDAP Servers ignore the second filter, some components don’t allow LDAP queries with two filters. Attackers then create special injections to obtain an LDAP query with a single filter. Now an injection like:
“value)(injected_filter”
Results in this filter:
(&(attribute=value)(injected_filter))(second_filter)).
How do attackers test an application to see if it’s vulnerable to code injections? They send a query to the server that generates an invalid input. If a server returns an error message, it means the server executed his query. Meaning code injection techniques are possible. Read more to find out about AND and OR injection environments.
AND LDAP Injection
In this case, the application constructs a query with the “&” operator. This together with one or more parameters that are introduced by the user is used to search in the LDAP directory.
(&(parameter1=value1)(parameter2=value2))
The search uses value1 and value2 as values that let the search in the LDAP directory happen. Hackers can maintain a correct filter construction while also injecting their malicious code. This is how they abuse the query to pursue their own objectives.
OR LDAP Injection
There are cases where the application makes a normal query with the (|) operator. Together with one or more parameters that the user introduces. An example looks something like this:
(|(parameter=value1)(parameter2=value2))
As before, value1 and value2 are used for the search.
BLIND LDAP Injections
Hackers can deduce a lot of things just from a server’s response. The application itself doesn’t show any error messages. Yet, the code that’s injected into the LDAP filter will generate a valid response or an error. A true result or a false result. Attackers exploit this behavior to obtain answers to true or false questions from the server. We call these techniques Blind Attacks. Even though blind LDAP Injection attacks aren’t as fast as classic ones, they are easy to implement. Why? Because they work on binary logic. Hackers use blind LDAP Injections to obtain sensitive information from the LDAP Directory.
AND Blind LDAP Injection
Imagine an online shop that can list all Puma shirts from an LDAP directory. But the error messages are not returned. This LDAP search filter gets sent:
(&(objectClass=Shirt)(type=Puma*))
Any available Puma shirts are shown to the user as icons. If there are no Puma shirts available, the user won’t see any icons. This is where Blind LDAP Injection comes into play. “*)objectClass=*))(&(objectClass=void”
is injected and now the application constructs an LDAP query that looks like:
(&(objectClass=*)(objectClass=*))(&(objectClass=void)(type=Puma*))
The server process only the (&(objectClass=*)(objectClass=*)) part of the LDAP filter. Now the shirt icon shows to the client. How so? The objectClass=* filter always returns an object. An icon showing means the response is true. Otherwise the response is false. The hackers now have the option of using blind injection techniques in many ways. An example of an injection:
(&(objectClass=*)(objectClass=users))(&(objectClass=foo)(type=Puma*))
(&(objectClass=*)(objectClass=Resources))(&(objectClass=foo)(type=Puma*))
Different objectClass values can be deduced with the help of these injections. If even a single shirt icon is shown, the objectClass value exists. Otherwise, the objectClass doesn’t exist. A hacker can obtain all sorts of information by using TRUE/FALSE questions via Blind LDAP injections.
OR Blind LDAP Injection
Injection in an OR environment looks like this:
(|(objectClass=void)(objectClass=void))(&(objectClass=void)(type=Puma*))
This LDAP query doesn’t obtain any objects from the LDAP directory service. The shirt icon doesn’t get shown to the client, making it a FALSE response. If an icon is shown it is a TRUE response. In order to gather information the hacker will inject an LDAP filter like this one:
(|(objectClass=void)(objectClass=users))(&(objectClass=void)(type=Puma*))
(|(objectClass=void)(objectClass=Resources))(&(objectClass=void)(type=Puma*))
It’s the same thing as with the AND Blind Injection. Keep reading to see how you can protect yourself against LDAP vulnerabilities!
How to Prevent LDAP Vulnerabilities
Unfortunately, firewalls and intrusion detection mechanisms will not help here as all of these attacks occur in the application layer. Your best option is to use minimum exposure points and minimum privileges principles.
Sanitize Inputs and Check Variables
The most effective way of preventing LDAP Injection attacks is to sanitize and check variables. As variables are the building block of LDAP filters, hackers use special characters in parameters to create malicious injections. AND “&”, OR “|”, NOT “!”, =, >=, <=, ~= are all operators that need to be filtered at the application layer to ensure they’re not used in Injection attacks.
All values which make the LDAP filter should be checked against a list of valid values in the Application Layer before the LDAP receives the query.
Don’t Construct Filters by Concatenating Strings
Avoid creating LDAP search filters by concatenating strings, if the string contains a user input. Instead, you should create the filter programmatically using the functionality provided by the LDAP library.
For example, in the Java UnboundID LDAP SDK, use this code to concatenate two strings provided by the user using an AND operator:
Filter filter = Filter.createANDFilter(
Filter.createEqualityFilter("cn", userInput),
Filter.createEqualityFilter("mail", userInput));
Creating an LDAP filter programmatically prevents malicious input from generating filter types that are different than expected. If the LDAP library you’re using doesn’t provide a way to programmatically create search filters, it is strongly recommended to replace it.
Use Access Control on the LDAP Server
To add another layer of protection, follow the principle of least privilege, and make sure that each account only has permission to perform operations needed for the user’s role.
For example, if you want your application to be able to search for items by looking for uid and mail attributes, only give the account permission to post searches for these attributes. Accounts should be granted read access to properties they need to retrieve but not modify. Before granting write access, make sure the application really needs to modify those properties.
If an application needs to process tasks on behalf of other users, you can use a proxied authentication request to ensure these tasks are handled according to the other user’s access control rights.
Restrict User Requests in Other Ways
Search filters are just one of the elements of an LDAP search request. You can work with other elements to reduce the risk of a malicious user request:
- Set the base DN and scope of the LDAP server to match as closely as possible to the type of search being performed. For example, if you want to search for users, and they are in a specific branch of the directory, restrict the search to that branch.
- Use a size limit to prevent the server from returning more items than expected. For example, when searching for individual user entries, if the search returns more than one entry, this will result in an error.
- Use timeouts to make sure your server doesn’t spend too much time processing searches. For most searches, a timeout of 1-2 seconds is sufficient. Set a timeout that is appropriate given your current search behavior and server performance, and you can avoid malicious searches querying large amounts of data, which may take more time.
Dynamic Application Security Testing
Dynamic Application Security Testing (DAST) can be used to automatically detect LDAP injection vulnerabilities.
Bright enables organizations to automate black-box testing for a long list of vulnerabilities for both applications and APIs. The various types of LDAP injection represent one of the vulnerabilities DAST solutions test for. As noted above it is crucial to detect LDAP vulnerabilities in the development process so they can be remediated early in the SDLC and not leave the organization exposed in production.
Bright’s ability to be run in an automated way as part of CI/CD ensures developers can detect these vulnerabilities early and remediate them before there is risk for the organization.
Learn more about preventing LDAP injection and other attacks with Bright
