In late 2018, through a series of coincidences, I became aware of an XSS vulnerability in the pie chart plugin made by Grafana. For those who are unaware, Grafana is a relatively popular data visualisation platform: you set up an instance, connect it to your data, and the promise is that Grafana will help you map that data in an easy way. I really like it - it forms the interface to a small weather station in my flat. The pie chart plugin is written by Grafana’s own devs, and is reasonably popular; as of writing, it sits near the top of Grafana’s plugins page.
The XSS vulnerability is not new, and there has been a Github issue regarding the vulnerability open for over three years now. After I was made aware of this, I created a pull request with a fix for the issue. After some initial activity, nothing has happened on the ticket. As it has been three years since initial disclosure and well over a month since I submitted my pull request, I decided to publish this article to provide a deeper dive into how this issue could be exploited to gain access to a Grafana cluster.
How the vulnerability works
The vulnerability itself is trivial. The pie chart doesn’t sanitise legend data
or tooltip data in any way, so virtually any basic XSS payload that is sent to
be displayed in the legend will execute in the browser’s context
(think </a><script>alert(1)</script>
, for example).
Exploiting the issue
Exploiting the issue remotely is much trickier. I am going to assume for this
exercise that you are conducting a red team exercise against an application that
you have already identified as Grafana (running on port 3000 can be one clue, as
that is the default port for a vanilla installation). In many scenarios, it is
probable that Grafana is being used as a visualisation and metrics engine for
web application traffic. I would recommend fuzzing web app endpoints heavily here, with payloads
that call back to a server under your control when they are loaded. If you get
a callback, you know that you’re in. It is probably wise to dump document.cookie
into the callback as well, to speed things up.
There are two cookie values that are most relevant for going further, grafana_sess
and grafana_remember
. I haven’t looked at the implementation of the cookie logic
closely, but grafana_sess
appears to be the session cookie, while grafana_remember
appears to be some sort of refresh token. I was initially disappointed, as the
grafana_sess
cookie is set to be HttpOnly
, which means you can’t extricate it
directly using your XSS.
As I soon discovered, however, you can use the grafana_remember
value (not HttpOnly
,
and therefore pilferable) and the username (also not HttpOnly
) to get yourself
a valid grafana_sess
value. A request like:
GET /login HTTP/1.1 Host: $YOUR_HOST_HERE Connection: close Cache-Control: max-age=0 Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Language: en-US,en;q=0.9 Cookie: grafana_remember=980132c3a59af478a353d3374ed9e3b9e87400d09c2e45e58a7ce613c22aef670f; grafana_user=$YOUR_USER_HERE
will get you a response like:
HTTP/1.1 302 Found Date: Tue, 05 Feb 2019 19:28:48 GMT Strict-Transport-Security: max-age=63072000; includeSubdomains; Content-Type: text/html; charset=utf-8 Location: /login Content-Length: 29 Set-Cookie: grafana_sess=f6ae05d3f6ecdde7; Path=/; HttpOnly Set-Cookie: redirect_to=%252Fd%252Fnew-dashboard-copy%253ForgId%253D1; Path=/; HttpOnly Connection: close Found.
With your grafana_sess
value from the response, you now have access
to all the application cookies you require to load the application as
an authenticated user.
What do you get?
The value in Grafana lies in the fact that it’s directly connected to databases.
Browse to /datasources
on an instance, and you should see DB configuration and
access details. At the very least this will be valuable information in planning
further attacks. A misconfigured Grafana instance could also give you the keys
to all of an app’s data.
Given its importance, I consider the XSS to be a fairly severe vulnerability. In
conjunction with the fact that you can bypass what little HttpOnly
protection
there is, I have been surprised by how little attention has been given to fixing
the issue.
How do you protect your Grafana instance?
- Apply the fix from my pull request. I am fairly sure it does what it says on the tin, although I haven’t received any feedback on it so ymmv.
- Stop using the pie chart plugin, although other XSS vulnerabilities probably exist in the platform.
- Make all the cookies used by Grafana
HttpOnly
- although that might break other functionality on the platform, I haven’t checked.
Postscript
Matt suggested that I applied for a CVE, which was surprisingly easy. Within a day, CVE-2015-9282 was granted, and within two hours of me notifying the developers, my pull request was accepted. Versions 1.3.6 of the plugin upwards are now fixed.