Checker Ldap   as of Julia version 2.5.0 (built on 4 Jul 2018)

belongs to group Basic

Identify potential LDAP poisoning attacks


Queries against Ldap databases return objects representing data stored in the database. Modifications to such objects should not be reflected into actual updates to the database, or otherwise everybody holding a reference to such objects might corrupt the database, in a kind of attack known as Ldap poisoning. This checker identifies such situations.

Action: Do not allow Ldap queries to return objects whose modification gets reflected into the database. Typically, a specific flag should not be set for such queries.

Examples


Consider the following program:

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.naming.Context;
import javax.naming.NameNotFoundException;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

import com.juliasoft.julia.extraction.EntryPoint;

public class LdapPoisoning {

  public @EntryPoint List<Object> search(int controls, String[] attributes, String base, String filter, String[] args) throws NamingException {
    LdapContext ctx = null;
    List<Object> result = new ArrayList<>();

    try {
      Properties env = createEnvironment();
      ctx = new InitialLdapContext(env, null);

      SearchControls ctls = new SearchControls();
      ctls.setSearchScope(controls);
      ctls.setReturningAttributes(attributes);
      ctls.setReturningObjFlag(true);

      NamingEnumeration<SearchResult> enm = ctx.search(base, filter, args, ctls);
      while (enm.hasMoreElements()) {
        SearchResult sr = enm.nextElement();
        result.add(sr.getObject());
      }
    }
    catch (NamingException ne) {
      throw ne;
    }
    finally {
      if (ctx != null)
        ctx.close();
    }

    return result;
  }

  public boolean exists(String dn) throws NamingException {
    Properties env = createEnvironment();
    LdapContext ctx = new InitialLdapContext(env, null);
    SearchControls ctls = new SearchControls();
    ctls.setSearchScope(SearchControls.OBJECT_SCOPE);
    ctls.setReturningAttributes(new String[0]);
    ctls.setReturningObjFlag(false);

    try {
      ctx.search(dn, "(objectClass=*)", ctls);
      return true;
    }
    catch (NameNotFoundException nne) {
      return false;
    }
  }

  protected Properties createEnvironment() {
    Properties env = new Properties();
    env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.setProperty(Context.PROVIDER_URL, "www.juliasoft.com");
    env.setProperty(Context.OBJECT_FACTORIES, "my.factory");
    env.setProperty(Context.SECURITY_PRINCIPAL, "user");
    env.setProperty(Context.SECURITY_CREDENTIALS, "verysecretpassword");

    return env;
  }
}

This checker issues the following warning:

LdapPoisoning.java:29: [Ldap: LDAPPoisoningWarning] A potential LDAP poisoning attack seems possible here

since the call to setReturningObjFlag(true) at line 29 allows method getObject() at line 34 to return an object whose modification gets reflected into the database. The object is stored into the list result returned at line 45 and hence accessible by the callers of method search(). Note that no warning is issued at line 54 instead.

In this example, the programmer should not call setReturningObjFlag(true) or should guarantee that the result of the search at line 31 does not escape from the local scope of the method.