#!/usr/bin/env python3 import argparse import requests import re G, B, R, W, M, C, end = '\033[92m', '\033[94m', '\033[91m', '\x1b[37m', '\x1b[35m', '\x1b[36m', '\033[0m' info = end + W + "[-]" + W good = end + G + "[+]" + C bad = end + R + "[" + W + "!" + R + "]" user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36" snapshot_endpoint = "api/snapshots" def send_request(url, method): """ sends an HTTP requset """ headers = {'User-Agent': user_agent} try: if method == "GET": r = requests.get(url, headers=headers) else: data = "" r = requests.post(url, headers=headers, data=data) except Exception as e: print(bad + " Problem with request! " + end) print(e) exit(-1) if (r.status_code == 302): print(bad + " Redirected. Try this instead: " + r.headers['Location'] + end) elif (r.status_code == 401): print(bad + " Status: " + str(r.status_code) + end) return(r.status_code) elif (r.status_code == 415): return(r.status_code) elif (r.status_code == 200): print(info + " Status: " + str(r.status_code) + end) return(r.text) else: print(info + " Something went wrong! " + end) print(bad + " Status: " + str(r.status_code) + str(r.content) + end) exit(-1) def check_version(grafana_server): """ checks the version of the grafana server """ response = send_request(grafana_server+"/login", "GET") print(info + " Checking for version..." + end) r1 = re.search('[0-9]{1}\.[0-9]{1}\.[0-9]{1}', str(response)) print(info + " Grafana version appears to be: " + r1.group(0) + end) target_version = r1.group(0) if "5." in target_version : fixed_version = '5.4.5' else: fixed_version = '6.3.4' if compare_versions(fixed_version, target_version) == False: print(bad + " Version seems to indicate it's probably not vulnerable." + end) else: print(good + " Version seems to indicate it might be vulnerable!" + end) def compare_versions(fixed_version, target_version): """ compares the version strings from the Grafana login page """ for i, j in zip(map(int, fixed_version.split(".")), map(int, target_version.split("."))): if i == j: continue return i > j return len(fixed_version.split(".")) > len(target_version.split(".")) def check_snapshots(grafana_server): """ checks if snapshot api allows unauthenticated requests """ print(info + " Checking if snapshot api requires authentiation..." + end) response = send_request(grafana_server+"/api/snapshots", "POST") if (response) == 401: print(bad + " Snapshot endpoint requires authentication! Host not vulnerable." + end) elif (response) == 415: print(good + " Snapshot endpoint doesn't seem to require authentication! Host may be vulnerable." + end) else: print(info + " Didn't received expected status code when checking snapshot API. Check again." + end) return def main(): """ main """ parser = argparse.ArgumentParser( prog='cve-2019-15043.py', description='For checking if a Grafana instance is vunlerable to CVE-2019-15043') parser.add_argument( "-u", "--url", help="URL of the target Grafana instance e.g. '-u https://localhost:3000'") parser.add_argument("-c", "--check-version", help="Only check the Grafana version", action='store_true') args = parser.parse_args() if not args.url: print(bad + " Missing parameters " + end) parser.print_help() exit(-1) url = str(args.url) print(info + " Testing " + url + "..." + end) if args.check_version == True: check_version(url) exit(0) else: check_version(url) check_snapshots(url) if __name__ == "__main__": main()