
Воспроизводимая лабораторная среда для CVE-2026-46716, критического межарендаторного RCE в Nezha Monitoring через обход авторизации cron API. Включает шаблон обнаружения Nuclei и Docker-кластеры с уязвимыми/исправленными версиями.
Лабораторная среда для воспроизведения и обнаружения CVE-2026-46716, критической уязвимости в Nezha Monitoring, где конечная точка POST /api/v1/cron не проверяет права администратора, позволяя пользователям с ролью RoleMember выполнять удаленный код в разных арендах.
| Поле | Подробности |
|---|
| CVE ID | CVE-2026-46716 |
| GHSA | GHSA-99gv-2m7h-3hh9 |
| CVSS | 9.9 (Critical) — AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H |
| Затронутые версии | nezhahq/nezha >= 1.4.0, < 1.14.15-0.20260517022419-d7526351cf97 |
| Исправлено | v1.14.15-0.20260517022419-d7526351cf97 (коммит d7526351cf97, 2026-05-17) |
| CWE | CWE-862 (Missing Authorization), CWE-269 (Improper Privilege Management), CWE-78 (OS Command Injection) |
Vulnerable path:
POST /api/v1/cron → commonHandler (JWT auth only, no role check)
→ CheckPermission(servers=[])
→ iterates empty list → returns true unconditionally
→ CronTrigger dispatches command to ALL servers via ServerShared map
❌ No admin check, no ownership validation → cross-tenant RCE
Patched path:
POST /api/v1/cron → commonHandler (same — HTTP 200 still returned for members)
→ CheckPermission(servers=[]) → true (unchanged)
→ CronTrigger: cronCanSendToServer(cr, s) checks cr.UserID == s.UserID
✅ Commands only dispatched to servers owned by the cron creator
| # | Условие | Подробности |
|---|---|---|
| 1 | Уязвимая версия Nezha | < 1.14.15-0.20260517022419 |
| 2 | Аутентифицированная учётная запись | RoleMember (минимальный уровень привилегий) |
| 3 | Пустой список серверов | "servers":[], "cover":1 обходит CheckPermission |
Host Machine
├── localhost:8008 ──→ Docker: nezha-vuln (v1.14.14 ⚠ VULNERABLE)
└── localhost:8009 ──→ Docker: nezha-patched (commit d7526351cf97 ✓ PATCHED, built from source)
| Инструмент | Установка |
|---|---|
| Docker Desktop | docker.com |
| nuclei | brew install nuclei |
| curl, python3 | предустановлены на macOS |
bash scripts/01-setup.sh
После завершения:
══════════════════════════════════════════════════════
Lab ready!
══════════════════════════════════════════════════════
Vulnerable (v1.14.14) : http://localhost:8008
Patched (latest) : http://localhost:8009
Admin : admin / admin
Member : member / Memberpass123!
══════════════════════════════════════════════════════
Примечание: Если API создания учётной записи участника отличается от ожидаемого, создайте её вручную через веб-интерфейс Nezha по адресу
http://localhost:8008(Настройки → Пользователи).
bash scripts/03-trigger-cve.sh
Ожидаемый вывод — уязвимая версия (v1.14.14):
HTTP 200 — Cron created (id: 1)
⚠ RESULT: ADMIN CHECK BYPASSED — VULNERABLE
Ожидаемый вывод — исправленная версия:
HTTP 403 — Access denied
✓ RESULT: Admin check enforced — PATCHED
# Уязвимый кластер → должен выдать [critical] finding
nuclei -duc -t nuclei/CVE-2026-46716.yaml \
-u http://localhost:8008 \
-var username=admin \
-var password=admin
# Исправленный кластер → не должен выдавать находки
nuclei -duc -t nuclei/CVE-2026-46716.yaml \
-u http://localhost:8009 \
-var username=admin \
-var password=admin
bash scripts/99-teardown.sh
nezha-cve-2026-46716-lab/
├── README.md
├── LAB_SETUP_GUIDE.md # Руководство по настройке лаборатории (Английский)
├── VULNERABILITY_ANALYSIS.md # Анализ уязвимости на уровне кода (Английский)
├── NUCLEI_TEMPLATE_GUIDE.md # Описание шаблона Nuclei и результаты тестов (Английский)
├── docker-compose.yaml
│
├── REPORT/ # Отчёты на корейском
│ ├── LAB_REPORT_KR.md
│ ├── Nuclei_Template_Report_KR.md
│ └── Vulnerability_Analysis_KR.md
│
├── nuclei/
│ └── CVE-2026-46716.yaml # Шаблон обнаружения Nuclei
│
└── scripts/
├── 01-setup.sh # Запуск контейнеров, создание учётной записи члена
├── 03-trigger-cve.sh # PoC: выполнение обхода авторизации, проверка результата
└── 99-teardown.sh # Остановка и удаление всех ресурсов лаборатории
Step 1 POST /api/v1/login (учётные данные администратора)
→ аутентификация и извлечение JWT токена
Step 2 GET /api/v1/setting
→ поле version сравнивается с уязвимым диапазоном (1.4.x – 1.14.14)
→ совпадение → НАХОДКА: обнаружена уязвимая версия
→ нет совпадения (>= 1.14.15 или "debug") → находка отсутствует
И уязвимая, и исправленная версии возвращают HTTP 200 для POST /api/v1/cron с
servers:[], cover:1 — поведенческое обнаружение невозможно. Исправление добавляет
только проверку владения внутри CronTrigger во время выполнения, а не на уровне API.
Требуются учётные данные администратора, так как GET /api/v1/setting возвращает
поле version только для пользователей с ролью RoleAdmin.
Предупреждение: Все учётные данные в этой лаборатории являются фиктивными тестовыми данными и предназначены исключительно для целей исследования безопасности. Никогда не используйте в рабочей среде.