5465 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / NUCLEI_TEMPLATE_GUIDE.md MD
# CVE-2026-46716 Nuclei Detection Template Guide

---

## 1. Overview

This document describes the design and test results of the nuclei template for CVE-2026-46716
(Nezha Monitoring CronTrigger delivery authorization bypass → Cross-Tenant RCE).

**Key discovery**: both vulnerable and patched versions return HTTP 200 for `POST /api/v1/cron`
with `servers:[], cover:1` from a member account — the API acceptance behavior is identical.
The patch only adds ownership validation inside CronTrigger at execution time. Therefore,
version-based detection via `GET /api/v1/setting` is used as the distinguishing signal.

---

## 2. Detection Approach

| Item | Details |
|------|---------|
| Detection type | **Version-based** — compares installed version against vulnerable range |
| Required credentials | Admin (version field in `/api/v1/setting` is admin-only) |
| Vulnerable indicator | `"version"` matches 1.4.x – 1.14.14 |
| Patched indicator | `"version"` is >= 1.14.15 or absent (e.g., `"debug"` for source builds) |
| Why not behavior-based | Both versions return HTTP 200 for member cron creation; actual execution difference requires connected agents |

---

## 3. Template Architecture

```
Step 1  POST /api/v1/login  (admin credentials)
        → Authenticate and extract JWT token
        → Internal matcher: body contains "data" and "token"
        → If match fails, step 2 is skipped (flow: http(1) && http(2))

Step 2  GET /api/v1/setting
        → Extract "version" field (only returned for admin-role users)
        → Regex match against vulnerable version range: 1.4.x – 1.14.14
        → Match → FINDING reported
        → No version match (>= 1.14.15 or "debug") → no finding
```

### Version Range Regex

```yaml
regex:
  - '"version":"1\.[4-9]\.[0-9]+"'    # 1.4.x – 1.9.x
  - '"version":"1\.1[0-3]\.[0-9]+"'  # 1.10.x – 1.13.x
  - '"version":"1\.14\.([0-9]|1[0-4])"'  # 1.14.0 – 1.14.14
```

---

## 4. Usage

```bash
# Scan vulnerable instance (admin credentials required)
nuclei -duc -u http://localhost:8008 \
  -t nuclei/CVE-2026-46716.yaml \
  -var username=admin -var password=admin

# Scan patched instance (should show 0 findings)
nuclei -duc -u http://localhost:8009 \
  -t nuclei/CVE-2026-46716.yaml \
  -var username=admin -var password=admin
```

---

## 5. Test Results

### Template Validation

```
nuclei -duc -validate -t nuclei/CVE-2026-46716.yaml
[INF] All templates validated successfully
```

### True-Positive (vulnerable v1.14.14)

```
nuclei -duc -u http://127.0.0.1:8008 \
  -t nuclei/CVE-2026-46716.yaml \
  -var username=admin -var password=admin

[CVE-2026-46716] [http] [critical] http://127.0.0.1:8008
[INF] Scan completed ... 1 matches found.
```

GET /api/v1/setting returns `"version":"1.14.14"` — matches the vulnerable range regex.

### False-Positive Validation (patched source build)

```
nuclei -duc -u http://127.0.0.1:8009 \
  -t nuclei/CVE-2026-46716.yaml \
  -var username=admin -var password=admin

[INF] Scan completed ... 0 matches found.
```

GET /api/v1/setting returns `"version":"debug"` for source builds — does not match the
version regex, so no finding is produced. Production patched deployments (>= v1.14.15)
also produce no finding.

---

## 6. Vulnerability Background

The vulnerability has two components:

**Component 1 — CheckPermission accepts empty server list without validation (present in both versions):**
`CheckPermission` iterates the provided server ID list to verify ownership. When `servers:[]`
is passed, the loop body never executes — no ownership check occurs and the function returns
`true`, allowing any authenticated user to create a cron with `cover:1` (all servers).

**Component 2 — CronTrigger delivery bypass (fixed in patch):**
Before patch: `CronTrigger` iterates all connected agents in `ServerShared` without
verifying that each agent belongs to the cron creator. With `cover:1`, every connected
agent receives the command — cross-tenant RCE.

After patch: `cronCanSendToServer()` checks `cr.UserID == server.UserID || userIsAdmin(cr.UserID)`
before each dispatch, restricting delivery to owned servers.

---

## 7. Important Notes

- Admin credentials are required for detection because `"version"` in `GET /api/v1/setting`
  is only returned for RoleAdmin users.
- The template does not create any cron jobs — it is read-only (login + version check).
- Always obtain proper authorization before scanning production systems.

---