TryHackMe · Writeup

Vulnversity

Busting directories, sneaking past an upload filter, and riding a SUID binary all the way to root.

By Kajal · Updated Aug 24, 2026 · ~6 min read
TryHackMe vulnversity recon webappsec privesc
Vulnversity room cover

This write-up covers a room from TryHackMe, built and hosted by the TryHackMe team.

Disclaimer. Neither the user flag nor the root flag is revealed here — you'll need to work through the steps yourself to grab them. Enjoy!

Task 1 — Deploy the Machine

Spin up the target machine and we're ready to begin.

Deploy the machine
No answer needed

Task 2 — Reconnaissance

Kick things off with an nmap scan against the target:

nmap -sV -p- {IP} -T4 -oN nmapbasic -vv
nmap scan output
nmap scan output continued

The scan comes back showing six open ports: 21, 22, 139, 445, 3128, and 3333.

Interestingly, the web server isn't sitting on the usual port 80 — it's tucked away on port 3333 instead. That's a good reminder to always scan well past the default 1024-port range during recon.

The scan also confirms the box is running Ubuntu. With that groundwork done, let's move on to the task questions.

Scan the box, how many ports are open?
Answer: 6
What version of the squid proxy is running on the machine?
Answer: 3.5.12
How many ports will nmap scan if the flag -p-400 was used?
Answer: 400
Using the nmap flag -n what will it not resolve?
Answer: DNS
What is the most likely operating system this machine is running?
Answer: Ubuntu
What port is the web server running on?
Answer: 3333
It's important to ensure you are always doing your reconnaissance thoroughly before progressing...
No answer needed

Task 3 — Locating Directories with Gobuster

Next, we point gobuster at the target on port 3333 to hunt for hidden directories:

gobuster dir -u <ip>:<port> -w <wordlist-path>
gobuster scan output

That turns up an /internal directory, and digging a little further reveals a nested /internal/uploads path.

Browsing to /internal, we land on a file upload form.

Upload form at /internal
What is the directory that has an upload form page?
Answer: /internal/

Task 4 — Compromising the Web Server

With an upload form in hand, the plan is simple: get a payload onto the server and execute it to gain a foothold.

Try upload a few file types to the server, what common extension seems to be blocked?
Answer: php

First step: test a range of file types to see what the form accepts and what it rejects. Since the backend runs on PHP, a PHP reverse shell is the natural payload choice.

Tip: tools like the Wappalyzer browser extension, or whatweb from a Kali terminal, make it easy to fingerprint the tech stack behind a site.
Testing file uploads

Sure enough, uploading a plain .php reverse shell gets blocked — the extension is filtered.

Upload rejected
To identify which extensions are not blocked, we're going to fuzz the upload form using BurpSuite.
No answer needed

Fire up Burp Suite and route browser traffic through it via FoxyProxy, then prepare a wordlist of alternate PHP extensions for a Sniper attack.

Preparing extension wordlist

Capture the upload request in Burp and forward it to Intruder.

Sending request to Intruder

Load the extension list in as a Simple List payload.

Loading payload list

Head to the Payloads tab and pick the Sniper attack type, then jump to Positions, locate the filename field, and mark the extension with § payload markers.

Setting Sniper payload position

Run the attack and review the results — every extension returns a 200 status, but the response length for .phtml stands out from the rest.

That's the tell. Uploading shell.phtml (our PHP reverse shell) goes through without a hitch.

Time to set up a listener on our attacking machine:

nc -lvnp 4444
Netcat listener

Navigate to /internal/uploads, trigger the uploaded file, and a reverse shell lands back on our listener.

Don't forget to turn off Burp's interceptor at this point.

Once inside, a quick id or whoami confirms which user we're running as. We're currently www-data.

A look through /etc/passwd turns up a user called bill, and sure enough there's a matching bill directory under /home.

Locating the bill user

Head into that directory to grab the user flag.

What is the name of the user who manages the webserver?
Answer: bill
What is the user flag?
Answer: 8xxxxxxxxxxxxxxxxxxxxxxxxxxxx

Task 5 — Privilege Escalation

With a foothold secured, the next goal is privilege escalation — climbing from www-data all the way to root.

There's more than one road to root here. It started with sudo -l to see what our user could run with elevated rights — nothing useful turned up. Cron jobs and kernel exploits were dead ends too, so the next stop was checking for exploitable SUID binaries.

Quick refresher: SUID (set owner user ID) is a special Linux file permission that lets a program run with the privileges of its owner rather than the user who launched it — a classic privilege-escalation vector when misconfigured.

Time to hunt for SUID-flagged binaries on the box. A tool like LinPEAS could automate this, but since we're only after the SUID bit, a simple find command does the job just as well:

find / -type f -perm -04000 -ls 2>/dev/null

This returns every file on the system with the SUID or SGID bit set.

SUID binaries found

One entry jumps out immediately: /bin/systemctl, the binary used to manage and monitor system services.

On the system, search for all SUID files. What file stands out?
Answer: /bin/systemctl
Become root and get the final flag (/root/root.txt)
Answer: a5xxxxxxxxxxxxxxxxxxxxxxxxxxxxd5

systemctl with SUID set is a known privilege-escalation path — a quick check on GTFOBins confirms it.

GTFOBins systemctl entry

The technique: spin up a temporary systemd service that reads root.txt and writes it out to /tmp/output, store the service definition in a variable called TF, then run it.

Creating the temporary service

Cat out /tmp/output and there's the root flag.

That gets us the flag, but our shell is still running as the low-privileged user. If you'd rather drop straight into a root shell instead, here's an alternative route:

Alternative root shell service
Alternative root shell service continued

Start a netcat listener on the port your service script points to (9001 here), run the service, and a root reverse shell calls back to your machine:

nc -lvnp 9001
Root reverse shell received

And that wraps it up — nicely done.