Data - Linux - Easy
-
Nmap scan reveals ports 3000 (HTTP) and 22 (SSH) are running.
-
Enumeration of the webserver reveals several important pieces of information. Firstly, Grafana’s version number can be found (and is out of date), which will allow searching for known exploits. Secondly, directory information can be enumerated with gobuster.
gobuster dir -w /usr/share/wordlists/dirbuster/directory-list-2.3-small.txt -u http://data.htb:3000 --exclude-length 29
-
We can use searchsploit to look for any known Grafana exploits within ExploitDB. One stands out, a directory traversal and arbitrary file read for Grafana version 8.3. After further investigation, the exploit appears to work for a number of versions of Grafana, including 8.0.0, the version running on the host.
searchsploit grafana
-
For directory traversal, the exploit uses the plugin folder to escape the webroot and read accessible files on the system unauthorized. Grafana ships with numerous default plugins which can be used as part of the path for the exploit, for example: alertlist. We can demonstrate this by crafting a web request to look at the /etc/passwd folder on the host.
Note: Notice there are almost no users with accessible shells on this system. Additionally, the root user’s shell is /bin/ash, not bash (Almquist shell, used as default on Alpine Linux distros). These two pieces of info could indicate that this webserver is within a docker container.
curl 'http://data.htb:3000/public/plugins/alertlist/../../../../../../../../etc/passwd' --path-as-is
-
Instances of Grafana have a database that stores its user information. Using this exploit, we can download the complete .db file from the website for offline cracking. To crack Grafana hashes, we must first convert them into a format understood by the cracking binary (hashcat or john), then run the attack. There are python scripts online which perform this step.
curl 'http://data.htb:3000/public/plugins/alertlist/../../../../../../../../var/lib/grafana/grafana.db' --path-as-is --output grafana.db
select email,password,salt from user;
hashcat -m 10900 hash /usr/share/wordlists/rockyou.txt
-
We now have credentials for a user account named Boris. Boris didn’t appear to be listed as a user on the host from the passwd file we read. However, remember that it’s possible this application is containerized. As such, we can attempt to log in with SSH on the host regardless. Fortunately we’re met with a nice surprise.
-
So that’s the first flag found. Now comes privesc. Some of the first commands I execute when I have access to a new account is checking what binaries I can run with elevated permissions, and SUID binaries. Before even trying SUIDs, we already get a hit for docker exec, which we can run with elevated permissions.
sudo -l
-
This next part took me some time as I’m not super familiar with Docker from an engineering perspective. Upon checking the file system mount points, we can see that sda1 partition is mounted on the root directory of the host system. This is not standard and a very sloppy way of accessing the host file system from within that partition. If we can mount sda1 from within the container, we can escape the container and run commands on the host with any permissions granted from within the docker instance.
-
The docker exec command can be run as the root user, with privileged access, using the following command to receive a shell into the docker container:
sudo docker exec -u root --privileged -ti Grafana "sh"
From there, mounting the drive is simple:
mount /dev/sda1 /mnt
-
Now we can cd into /mnt and find the host file system, except we’re running with root permissions. From here, you could many things for persistence, such as making a new user and adding them to sudoers, or a 2nd root account. Since this is a lab exercise however, we’ll simply cat out the root.txt flag and complete the box.