Summary
Intro Hey everyone! Today we’re solving the TryHackMe room RootMe — a great beginner-friendly box that covers web enumeration, exploiting a file upload vulnerability to get a reverse shell, and then escalating privileges to root using a SUID binary. I’ll walk you through every single step, exactly how I did it, with simple explanations for each command so you actually understand why we’re running it, not just copy-pasting. Let’s go! 😄 Task 1 — Deploy the Machine First things first — deploy the machine and connect to the TryHackMe VPN (or just use the AttackBox if you prefer). Once connected, you’ll get assigned a target IP, and that’s what we’ll be attacking throughout this room. My target IP was: 10.48.145.206 Task 2 — Reconnaissance (Information Gathering) Nmap Scan The very first thing to do on any box — run an nmap scan to see what ports are open and what’s running on them. nmap -sC -sV -T5 10.48.145.206 -oN nmapresult.txt Breaking down the command: -sC → Runs Nmap’s default scripting engine scripts, which gives extra useful details (cookies, headers, sometimes even vulnerabilities).-sV → Detects the version of whatever service is running on each open port.-T5 → Sets the scan speed to the fastest (T0 is slowest, T5 is fastest).-oN nmapresult.txt → Saves the output to a file so you can refer back to it later. Result: Two ports were open: Port Service Version 22 SSH OpenSSH 8.2p1 Ubuntu 80 HTTP Apache 2.4.41 (Ubuntu) One small but important detail from the -sC scripts — the site was setting a PHPSESSID cookie. That’s a dead giveaway that the backend is written in PHP, since that’s PHP’s default session cookie name. Q: Scan the machine, how many ports are open? → 2 Q: What version of Apache is running? → 2.4.41 Q: What service is running on port 22? → SSH (Small confession — I almost skipped noting the PHPSESSID detail the first time, but it ended up being pretty useful later when picking which reverse shell to use. Little details like this matter more than they seem!) Gobuster — Finding Hidden Directories Next, let’s find out if there are any hidden pages or folders on the web server using Gobuster. gobuster dir -u http://10.48.145.206/ -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt Breaking down the command: dir → Tells Gobuster to run in directory brute-force mode.-u → The target URL.-w → Path to the wordlist — a big list of common folder/file names that Gobuster will try one by one. Directories found: /uploads /css /js /panel /server-status (403 forbidden — no access) Q: Find directories on the web server using the GoBuster tool. → Use the Gobuster command shown above Q: What is the hidden directory? → /panel Task 3 — Getting a Shell Exploring the Website Time to open these directories in the browser. /css and /js were nothing special (just static assets), but /panel had a file upload form. So the site allows file uploads, and uploaded files land in the /uploads folder. Testing the Upload Before jumping straight to an exploit, I first uploaded a harmless test file (a .png image) just to confirm the upload feature actually worked. Upload was successful, and I double-checked by visiting /uploads/ — the file was sitting right there. Now that we know files can be uploaded, the obvious next move is trying to upload something that gives us code execution — since we already know the backend runs PHP. Preparing a PHP Reverse Shell I grabbed a well-known PHP reverse shell script (the classic pentestmonkey one — very commonly used in CTFs). Only two things need to change in it: port → The port you’ll be listening on To get your IP: ifconfig If you’re connected through the TryHackMe VPN, make sure you grab the IP from the tun0 interface — not eth0 — since the VPN tunnel is what actually routes traffic to the target machine. First Attempt — Permission Denied I uploaded the shell with a plain .php extension first, and the server immediately rejected it: “PHP não é permitido!” (PHP is not allowed!) So there’s clearly a server-side filter blocking .php files specifically. Bypassing the Filter A quick search turned up a few common bypass tricks for exactly this situation:
- Change the extension: .php5 ,.phtml ,.pht ,.phps
- Double extension trick: image.php.jpg
- Null byte trick: file.php%00.jpg (works on older/outdated servers) I renamed the file to .php5 : mv php-reverse-shell.php php-reverse-shell.php5 Upload Successful! This time it went through cleanly — no error, and the message changed to “O arquivo foi upado com sucesso!” (File uploaded successfully!) Checked /uploads/ again and there it was: Setting Up the Listener Before actually triggering the shell, you need a netcat listener running on your machine so it can catch the incoming connection the moment the file executes. netcat -knlvp 4444 Breaking down the command: -k → Keeps the listener alive even after a connection closes (so it can accept another one if needed).-n → Skips DNS resolution — since we’re expecting a numeric IP, not a hostname, and this also speeds things up.-l → Puts netcat into listen mode — it just waits for an incoming connection.-v → Verbose mode, so you see everything happening in detail.-p 4444 → The port to listen on (must match the port set inside the PHP shell script). Triggering the Reverse Shell Now open the uploaded file’s URL in the browser: http://10.48.145.206/uploads/php-reverse-shell.php5 Get Krish Gupta’s stories in your inbox Join Medium for free to get updates from this writer. The first time I opened it, there was a minor error (something about failing to daemonise) — totally normal, nothing to worry about. Tried again, switched back to the terminal running the listener, and there it was — a shell! 🎉 Ran whoami and got www-data — the low-privilege user that Apache runs as. Finding user.txt Time to grab the user.txt flag: find / -type f -name “user.txt” 2>/dev/null Breaking down the command: find / → Search the entire filesystem, starting from root.-type f → Only look for files (not directories).-name “user.txt” → Match files named exactlyuser.txt .2>/dev/null → Silences all error messages (like “Permission denied”) so the output stays clean. Here2 refers to the stderr (error) stream, and/dev/null is basically a black hole — anything sent there just disappears. Found it in /var/www/user.txt .✅ user.txt captured! 🎉 Task 4 — Privilege Escalation (Getting to Root) We’re currently www-data , which has limited privileges. To get root, we need to find some privilege escalation path. Hunting for SUID Binaries A SUID (Set User ID) bit is a special permission that, when set on an executable, makes it run with the file owner’s privileges — no matter who actually runs it. If a SUID binary is owned by root and has some kind of weakness, that’s a golden ticket to becoming root. Find all SUID binaries with: find / -perm -4000 2>/dev/null Breaking down the command: -perm -4000 →4000 is the octal value representing the SUID bit, so this finds every file with that bit set.2>/dev/null → Same as before, hides error clutter and drop the all error in recyclebin . Most of the list was standard system stuff, but one entry immediately looked out of place — /usr/bin/python2.7 had the SUID bit set! Having SUID set on a programming language interpreter like Python is very unusual and almost always a misconfiguration worth exploiting. ✅ Q: Search for files with SUID permission, which file is weird? → python (python2.7) Finding the Exploit on GTFOBins Next step — figure out how to actually abuse this. GTFOBins (gtfobins.org) is the go-to resource for this — it’s basically a cheat sheet listing exploitation techniques for common Unix binaries. Searched “python” on GTFOBins, clicked the SUID tab, and found this: python -c ‘import os; os.execl(“/bin/sh”, “sh”, “-p”)’ Breaking down the command: import os → Loads Python’sos module, which lets us interact with operating system-level operations.os.execl(“/bin/sh”, “sh”, “-p”) → Replaces the current process with/bin/sh (a shell).-p → Privileged mode — tells the shell to preserve the current effective user/group ID. Since the Python binary is SUID and owned by root, this means the shell it spawns keeps that root-level effective ID too — giving us a root shell. ⚠️ A little slip-up worth mentioning: I initially typed os.excel() by mistake (typo — should beexecl , notexcel ), which threw anAttributeError . Happens to the best of us — just double-check the method name if you hit this error! Running the correct command: whoami now showed root! 🎉 ✅ Q: Find a form to escalate your privileges. → GTFOBins’ Python SUID exploit Finding root.txt Grab the final flag: find / -type f -name “root.txt” 2>/dev/null cat /root/root.txt Found in /root/root.txt . 🚩 ✅ root.txt captured! 🎉 Conclusion Quick recap of everything we covered in this room:
- Using Nmap for basic recon on a target.
- Using Gobuster to discover hidden web directories.
- Identifying a file upload vulnerability and bypassing the extension filter.
- Deploying a PHP reverse shell to get an initial foothold.
- Spotting a SUID misconfiguration and using GTFOBins to escalate to root. RootMe is a great example of how small, individually harmless-looking misconfigurations (a weak upload filter here, a misplaced SUID bit there) can chain together into a full system compromise. This is exactly why every little detail matters during an assessment — even the ones you almost overlook. Hope this walkthrough was clear and easy to follow! If you have any questions or feedback, feel free to connect with me on LinkedIn: Happy Hacking! 🔐