The Lightweight Directory Access Protocol (LDAP) allows an application to remotely perform operations such as searching and modifying records existing in directories. LDAP injection results from inadequate input sanitization and validation and allows malicious users to glean restricted information using the directory service.
A white-list can be used to restrict input to a list of valid characters. The list of characters that must not be allowed in a white-list include JNDI meta-characters and LDAP special characters. They are tabulated below:
Character |
Name |
---|---|
' and " |
Single and double quote |
/ and \ |
Forward-slash and back-slash |
\ \ |
Double slashes* |
space |
Space character at beginning or end of string |
# |
Hash character at the beginning of the string |
< and > |
Angle brackets |
, and ; |
Comma and semi-colon |
+ and * |
Addition and multiplication operators |
( and ) |
Round braces |
\u0000 |
Unicode NULL character |
- This is a character sequence
Noncompliant Code Example
For the purpose of this example, consider an LDAP Data Interchange Format (LDIF) file that contains records in the following format:
dn: dc=example,dc=com objectclass: dcobject objectClass: organization o: Some Name dc: example dn: ou=People,dc=example,dc=com ou: People objectClass: dcobject objectClass: organizationalUnit dc: example dn: cn=Manager,ou=People,dc=example,dc=com cn: Manager sn: John Watson # Several objectClass definitions here (omitted) userPassword: secret1 mail: john@holmesassociates.com dn: cn=Senior Manager,ou=People,dc=example,dc=com cn: Senior Manager sn: Sherlock Holmes # Several objectClass definitions here (omitted) userPassword: secret2 mail: sherlock@holmesassociates.com
This noncompliant code example allows a caller of the method searchRecord()
to search for a record in the directory using the LDAP protocol. The string filter
is responsible for filtering the result set on the basis of a user name and password that the caller must supply. If a malicious user enters specially crafted input, this elementary authentication scheme fails to confine the output of the search query to the information that the user is privileged to access. For example, the user may see any record beginning with "S" by supplying the values S*
and *
for the string variables userSN
and UserPassword
respectively. Consequently, information about any user can be gleaned without any prior knowledge of a particular user name and password pair.
// String userSN = "S*"; // Invalid // String userPassword = "*"; // Invalid public class LDAPInjection { private void searchRecord(String userSN, String userPassword) throws NamingException { Hashtable env = new Hashtable(); env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory"); String name = "dc=example,dc=com"; try { Context ctx = new InitialContext(env); DirContext dctx = new InitialDirContext(env); SearchControls sc = new SearchControls(); String[] attributeFilter = {"cn", "mail"}; sc.setReturningAttributes(attributeFilter); sc.setSearchScope(SearchControls.SUBTREE_SCOPE); String base = "dc=example,dc=com"; // The following resolves to (&(sn=S*)(userPassword=*)) String filter = "(&(sn=" + userSN + ")(userPassword=" + userPassword + "))"; NamingEnumeration results = dctx.search(base, filter, sc); while (results.hasMore()) { SearchResult sr = (SearchResult) results.next(); Attributes attrs = sr.getAttributes(); Attribute attr = attrs.get("cn"); System.out.println(attr.get()); attr = attrs.get("mail"); System.out.println(attr.get()); } dctx.close(); } catch (NamingException e) { // Handle } } }
Compliant Solution
This compliant solution uses a white-list to validate user input so that only valid characters appear in the filter
string. For example, userSN
may contain only letters and spaces whereas a password may also contain alphanumeric characters.
// String userSN = "Sherlock Holmes"; // Valid // String userPassword = "secret2"; // Valid sc.setSearchScope(SearchControls.SUBTREE_SCOPE); String base = "dc=example,dc=com"; if(!userSN.matches("[a-zA-Z\\s]*") || !userPassword.matches("[a-zA-Z0-9]*")) { throw new IllegalArgumentException("Invalid input"); } String filter = "(&(sn = " + userSN + ")(userPassword=" + userPassword + "))";
If it is desired to include special characters in a database field such as a password, it is critical to ensure that the authentic data is stored in a sanitized form in the database and any user input is escaped and transformed into the equivalent form, before the validation or comparison takes place. The use of characters that have special meanings in JNDI and LDAP is strongly discouraged unless a comprehensive white-listing based routine is employed to encode and escape the characters. Refer to the guideline IDS04-J. Properly encode or escape output for examples on output encoding and escaping. The special character must be transformed to a sanitized safe value before adding it to the white-list expression against which input is required to be validated. Likewise, sanitization of user input (escaping and encoding) should occur before the validation step.
Risk Assessment
Failing to sanitize untrusted input can result in information disclosure and privilege escalation.
Rule |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
IDS16- J |
high |
likely |
medium |
P18 |
L1 |
Automated Detection
TODO
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[API 06]]
[[OWASP 08]] Preventing LDAP Injection in Java
FIO05-J. Do not create multiple buffered wrappers on an InputStream 09. Input Output (FIO) 09. Input Output (FIO)