Exploiting XSS in Grafana's piechart-panel

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?

  1. 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.
  2. Stop using the pie chart plugin, although other XSS vulnerabilities probably exist in the platform.
  3. 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.

*****
Written by Feroz Salam on 05 February 2019