...
Consider an LDAP Data Interchange Format (LDIF) file that contains records in the following format:
Code Block |
---|
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
|
A search for a valid user name and password often takes the form:
Code Block |
---|
(&(sn=<USERSN>)(userPassword=<USERPASSWORD>))
|
...
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 used to filter the result set for those entries that match a user name and password supplied by the caller. When a malicious user enters specially crafted input, as outlined previously, this elementary authentication scheme fails to confine the output of the search query to the information for which the user has access privileges.
Code Block | ||
---|---|---|
| ||
// String userSN = "S*"; // Invalid
// String userPassword = "*"; // Invalid
public class LDAPInjection {
private void searchRecord(String userSN, String userPassword) throws NamingException {
Hashtable<String, String> env = new Hashtable<String, String>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
try {
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
}
}
}
|
...
This compliant solution uses a whitelist to sanitize user input so that the filter
string contains only valid characters. In this code, userSN
may contain only letters and spaces, whereas a password may contain only alphanumeric characters.
Code Block | ||
---|---|---|
| ||
// String userSN = "Sherlock Holmes"; // Valid
// String userPassword = "secret2"; // Valid
sc.setSearchScope(SearchControls.SUBTREE_SCOPE);
String base = "dc=example,dc=com";
if(!userSN.matches("[\\w\\s]*") || !userPassword.matches("[\\w]*")) {
throw new IllegalArgumentException("Invalid input");
}
String filter = "(&(sn = " + userSN + ")(userPassword=" + userPassword + "))";
|
...
Guideline | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
IDS11IDS51-J | high | likely | medium | P18 | L1 |
Related Vulnerabilities
...