When reports surface that the White House and NATO were hacked using an unknown vulnerability, it peaked our interest. When it was later announced that the Java Naming and Directory Interface (JNDI) was used, we decided to look at other Java APIs to see if we could find other vulnerabilities. We expected to find a bug or two. What we actually found was two new classes of vulnerabilities: JNDI Injection and LDAP Entry Poisoning.
Java Naming and Directory Interface (JNDI) is a Java API that allows clients to discover and look up data and objects via a name. These objects can be stored and retrieved through different naming or directory services such as Remote Method Invocation (RMI), Common Object Request Broker Architecture (CORBA), Lightweight Directory Access Protocol (LDAP), or Domain Name Service (DNS).
The lesson learned from the “Click-to-Play” bypass technique is that if an attacker can control the argument to a JNDI lookup operation, they will be able to execute arbitrary remote code on the server performing the lookup. The attacker will be able to do so by pointing the lookup to a Naming or Directory service under his/her control and returning a JNDI reference that uses a remote factory for object instantiation. Although JNDI Injection may not be very common in modern web applications, we think it may be a real problem for enterprise-level applications with possible attack vectors spanning RMI, CORBA, and LDAP.
While we found JNDI Injection to be interesting, a different investigation led to the discovery of a greater problem: LDAP Entry Poisoning. While investigating whether or not actual applications are vulnerable using InitialDirContext.lookup() calls with untrusted data, we realized that it is not a very frequent operation in Directory Services. Most of the operations are done on attributes rather than at the object level. For example, instead of looking for an object with a lookup() call, the search() method is used to retrieve the desired attributes of the LDAP entry (e.g.: username, password, email, etc…). When only attributes are requested, there will be no Java object decoding that can compromise the server. However, there is a special flag that can be switched on for searches: the returnObjFlag. If an application performs a search operation with the returnObjFlag set to true, an attacker controlling the LDAP response will be able to execute arbitrary commands on the application server.
Modifying an entry in the LDAP server or search response and injecting the Java attributes for a serialized object or JNDI reference will make the LdapSearchEnumeratio decode the response as a Java object, and therefore allow the execution of Java code by injecting a JNDI reference with a user-controlled object factory. The question is, how popular are the search queries with returnObjectFlag set to true? We found that it is quite common. In most of the cases, the developers are not looking for decoding any Java objects, they are just looking for the DirContext wrapper of the returned result.
Our presentation and paper take you through the details of these attacks. We also provide defenders with guidance on preventing these types of attacks. Enterprises should consider the following from a defensive perspective:
- Do not pass untrusted data to an lookup() method.
- When using a Security Manager, carefully audit its Policy.
- If possible, do not allow remote codebases.
- When integrating with an LDAP server try to avoid object-returning queries.
- Use static analysis code reviews to find “JNDI Injection” and “LDAP Entry Poisoning” in an efficient and scalable way.
- Carefully protect your LDAP backends since they contain the keys to your kingdom.
From a penetration tester perspective:
- Fuzz your web applications with different JNDI payloads to verify they are not taking untrusted data into APIs that perform lookup operations.
- Poison a controlled user and use the account to log in on every LDAP integrated service tofind out which applications are vulnerable.