HAERIN-L/POC_CVE-2026-46716

GitHub: HAERIN-L/POC_CVE-2026-46716

Stars: 0 | Forks: 0

# CVE-2026-46716 — Nezha Monitoring Cross-Tenant RCE via Cron API Authorization Bypass A lab environment for reproducing and detecting **CVE-2026-46716**, a critical vulnerability in Nezha Monitoring where the `POST /api/v1/cron` endpoint lacks admin privilege checks, allowing RoleMember users to achieve cross-tenant remote code execution. ## Vulnerability Overview | Field | Details | |-------|---------| | CVE ID | CVE-2026-46716 | | GHSA | [GHSA-99gv-2m7h-3hh9](https://github.com/nezhahq/nezha/security/advisories/GHSA-99gv-2m7h-3hh9) | | CVSS | 9.9 (Critical) — `AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H` | | Affected versions | nezhahq/nezha >= 1.4.0, < 1.14.15-0.20260517022419-d7526351cf97 | | Patched | v1.14.15-0.20260517022419-d7526351cf97 (commit d7526351cf97, 2026-05-17) | | CWE | CWE-862 (Missing Authorization), CWE-269 (Improper Privilege Management), CWE-78 (OS Command Injection) | ### Root Cause 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 ### Attack Prerequisites | # | Condition | Details | |---|-----------|---------| | 1 | Affected Nezha version | < 1.14.15-0.20260517022419 | | 2 | Authenticated account | `RoleMember` (lowest privilege level) | | 3 | Empty server list | `"servers":[], "cover":1` bypasses CheckPermission | ## Lab Architecture Host Machine ├── localhost:8008 ──→ Docker: nezha-vuln (v1.14.14 ⚠ VULNERABLE) └── localhost:8009 ──→ Docker: nezha-patched (commit d7526351cf97 ✓ PATCHED, built from source) ## Prerequisites | Tool | Install | |------|---------| | [Docker Desktop](https://www.docker.com/) | docker.com | | [nuclei](https://github.com/projectdiscovery/nuclei) | `brew install nuclei` | | curl, python3 | pre-installed on macOS | ## How to Run ### Step 1 — Set up lab environment bash scripts/01-setup.sh When done: ══════════════════════════════════════════════════════ Lab ready! ══════════════════════════════════════════════════════ Vulnerable (v1.14.14) : http://localhost:8008 Patched (latest) : http://localhost:8009 Admin : admin / admin Member : member / Memberpass123! ══════════════════════════════════════════════════════ ### Step 2 — Trigger the CVE bash scripts/03-trigger-cve.sh **Expected output — vulnerable (v1.14.14):** HTTP 200 — Cron created (id: 1) ⚠ RESULT: ADMIN CHECK BYPASSED — VULNERABLE **Expected output — patched:** HTTP 403 — Access denied ✓ RESULT: Admin check enforced — PATCHED ### Step 3 — Nuclei detection # Vulnerable cluster → should produce a [critical] finding nuclei -duc -t nuclei/CVE-2026-46716.yaml \ -u http://localhost:8008 \ -var username=admin \ -var password=admin # Patched cluster → should produce no findings nuclei -duc -t nuclei/CVE-2026-46716.yaml \ -u http://localhost:8009 \ -var username=admin \ -var password=admin ### Step 4 — Teardown bash scripts/99-teardown.sh ## Directory Structure nezha-cve-2026-46716-lab/ ├── README.md ├── LAB_SETUP_GUIDE.md # Lab setup guide (English) ├── VULNERABILITY_ANALYSIS.md # Code-level vulnerability analysis (English) ├── NUCLEI_TEMPLATE_GUIDE.md # Nuclei template design and test results (English) ├── docker-compose.yaml │ ├── REPORT/ # Korean reports │ ├── LAB_REPORT_KR.md │ ├── Nuclei_Template_Report_KR.md │ └── Vulnerability_Analysis_KR.md │ ├── nuclei/ │ └── CVE-2026-46716.yaml # Nuclei detection template │ └── scripts/ ├── 01-setup.sh # Start containers, create member account ├── 03-trigger-cve.sh # PoC: trigger auth bypass, verify result └── 99-teardown.sh # Stop and remove all lab resources ## Nuclei Template Detection Logic Step 1 POST /api/v1/login (admin credentials) → authenticate and extract JWT token Step 2 GET /api/v1/setting → version field matched against vulnerable range (1.4.x – 1.14.14) → match → FINDING: vulnerable version detected → no match (>= 1.14.15 or "debug") → no finding Both vulnerable and patched versions return HTTP 200 for `POST /api/v1/cron` with `servers:[], cover:1` — behavioral detection is not possible. The patch only adds ownership validation inside CronTrigger at execution time, not at the API level. Admin credentials are required because `GET /api/v1/setting` returns the `version` field only for RoleAdmin users. ## References - [GHSA-99gv-2m7h-3hh9](https://github.com/nezhahq/nezha/security/advisories/GHSA-99gv-2m7h-3hh9) - [NVD — CVE-2026-46716](https://nvd.nist.gov/vuln/detail/CVE-2026-46716)