CVE-2026-59155
Nezha Monitoring: DDNS and Notification credential exposure via unredacted list API
Nezha Monitoring is a self-hostable, lightweight, servers and websites monitoring and O&M tool. Prior to 2.2.5, the GET /api/v1/ddns and GET /api/v1/notification endpoints return full resource objects including plaintext third-party API credentials, including Cloudflare API tokens, TencentCloud SecretKeys, Slack, Discord, and Telegram webhook URLs with embedded bot tokens, and Authorization header values, without any field-level redaction. Any authenticated admin or PAT with nezha:ddns:read or nezha:notification:read scope can receive stored credentials through the listDDNS and listNotification handlers in a single API response. This issue is fixed in version 2.2.5.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- High
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- None
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
6.9
Nezha Monitoring: DDNS and Notification credential exposure via unredacted list API
Nezha Monitoring is a self-hostable, lightweight, servers and websites monitoring and O&M tool. Prior to 2.2.5, the GET /api/v1/ddns and GET /api/v1/notification endpoints return full resource objects including plaintext third-party API credentials, including Cloudflare API tokens, TencentCloud SecretKeys, Slack, Discord, and Telegram webhook URLs with embedded bot tokens, and Authorization header values, without any field-level redaction. Any authenticated admin or PAT with nezha:ddns:read or nezha:notification:read scope can receive stored credentials through the listDDNS and listNotification handlers in a single API response. This issue is fixed in version 2.2.5.
- Record
CVE-2026-59155- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-200
- Updated
- 2026-11-02
EUVDEUVD-2026-43108
6.9
Nezha Monitoring is a self-hostable, lightweight, servers and websites monitoring and O&M tool. Prior to 2.2.5, the GET /api/v1/ddns and GET /api/v1/notification endpoints return full resource objects including plaintext third-party API credentials, including Cloudflare API tokens, TencentCloud SecretKeys, Slack, Discord, and Telegram webhook URLs with embedded bot tokens, and Authorization header values, without any field-level redaction. Any authenticated admin or PAT with nezha:ddns:read or nezha:notification:read scope can receive stored credentials through the listDDNS and listNotification handlers in a single API response. This issue is fixed in version 2.2.5.
- Record
EUVD-2026-43108- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-ww5p-j6cj-6mqq
5.5
Nezha Dashboard: DDNS and Notification credential exposure via unredacted list API
Summary
The GET /api/v1/ddns and GET /api/v1/notification endpoints return full resource objects including plaintext third-party API credentials — Cloudflare API tokens, TencentCloud SecretKeys, Slack/Discord/Telegram webhook URLs with embedded bot tokens, and Authorization header values — without any field-level redaction. Any authenticated admin who calls these endpoints receives every stored credential in the system in a single API response. A compromised admin session or leaked PAT with nezha:ddns:read or nezha:notification:read scope exposes all third-party integration secrets.
Details
The listDDNS and listNotification handlers follow an identical pattern: they call the corresponding singleton GetSortedList(), copier.Copy the full in-memory structs into a response slice, and return them via listHandler with zero field stripping.
DDNS — cmd/dashboard/controller/ddns.go:25–33:
func listDDNS(c *gin.Context) ([]*model.DDNSProfile, error) {
var ddnsProfiles []*model.DDNSProfile
list := singleton.DDNSShared.GetSortedList()
if err := copier.Copy(&ddnsProfiles, &list); err != nil {
return nil, err
}
return ddnsProfiles, nil
}
The DDNSProfile struct (model/ddns.go:20–36) serializes AccessSecret with json:"access_secret,omitempty" — non-empty Cloudflare tokens and TencentCloud SecretKeys are returned in cleartext. The WebhookURL and WebhookHeaders fields may also contain embedded secrets.
Notification — cmd/dashboard/controller/notification.go:25–33:
func listNotification(c *gin.Context) ([]*model.Notification, error) {
slist := singleton.NotificationShared.GetSortedList()
var notifications []*model.Notification
if err := copier.Copy(¬ifications, &slist); err != nil {
return nil, err
}
return notifications, nil
}
The Notification struct (model/notification.go:34–44) serializes URL, RequestHeader, and RequestBody — all of which commonly contain embedded bot tokens (Slack, Discord, Telegram), API keys in Authorization headers, and webhook secrets.
Route and authorization (cmd/dashboard/controller/controller.go:155, 171):
auth.GET("/notification", restScopeMiddleware(model.ScopeNotificationRead), listHandler(listNotification))
auth.GET("/ddns", restScopeMiddleware(model.ScopeDDNSRead), listHandler(listDDNS))
Both routes are behind authMw (JWT or PAT) and the corresponding read scope. The listHandler → filter chain uses HasPermission (model/common.go:63–82) which grants admins access to ALL profiles and restricts members to their own. No separate response struct or field masking exists anywhere in the codebase — confirmed by exhaustive search for DDNSResponse, DDNSView, NotificationResponse, NotificationView, or any JSON middleware that strips sensitive fields.
The codebase already demonstrates awareness of this pattern: serverConfigSensitiveScope() in cmd/dashboard/controller/api_token_scope.go:117 was introduced to restrict client_secret exposure via GET /server/config/:id, tightening the scope from ScopeServerRead to ScopeServerWrite. No equivalent protection exists for the DDNS or Notification list endpoints.
Tested at commit 3d74cd94 (master, post v2.2.3). The vulnerable pattern has existed since the DDNS and notification list endpoints were introduced.
PoC
-
Deploy nezha with at least one admin user. Configure a DDNS profile with a Cloudflare API token (AccessSecret) and a Notification webhook pointing to a Slack incoming webhook URL (
https://hooks.slack.com/services/T.../B.../xxx...). -
Authenticate as the admin user. Call:
# DDNS credentials exposed curl -s -H "Authorization: Bearer <admin_jwt>" \ https://dashboard.example.com/api/v1/ddns \ | jq '.data[].access_secret' # Notification webhook secrets exposed curl -s -H "Authorization: Bearer <admin_jwt>" \…
- Record
GHSA-ww5p-j6cj-6mqq- CVSS 4.0
- 5.5 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:P - Weakness
- CWE-200
- Updated
- 2026-11-02
NVD
6.9
Nezha Monitoring is a self-hostable, lightweight, servers and websites monitoring and O&M tool. Prior to 2.2.5, the GET /api/v1/ddns and GET /api/v1/notification endpoints return full resource objects including plaintext third-party API credentials, including Cloudflare API tokens, TencentCloud SecretKeys, Slack, Discord, and Telegram webhook URLs with embedded bot tokens, and Authorization header values, without any field-level redaction. Any authenticated admin or PAT with nezha:ddns:read or nezha:notification:read scope can receive stored credentials through the listDDNS and listNotification handlers in a single API response. This issue is fixed in version 2.2.5.
- Record
CVE-2026-59155- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X - Weakness
- CWE-200
- Updated
- 2026-11-02
OSV
6.9
Nezha Monitoring: DDNS and Notification credential exposure via unredacted list API
Nezha Monitoring is a self-hostable, lightweight, servers and websites monitoring and O&M tool. Prior to 2.2.5, the GET /api/v1/ddns and GET /api/v1/notification endpoints return full resource objects including plaintext third-party API credentials, including Cloudflare API tokens, TencentCloud SecretKeys, Slack, Discord, and Telegram webhook URLs with embedded bot tokens, and Authorization header values, without any field-level redaction. Any authenticated admin or PAT with nezha:ddns:read or nezha:notification:read scope can receive stored credentials through the listDDNS and listNotification handlers in a single API response. This issue is fixed in version 2.2.5.
- Record
CVE-2026-59155- CVSS 4.0
- 6.9 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N - Weakness
- CWE-200
- Updated
- 2026-11-02
OSVGHSA-ww5p-j6cj-6mqq
5.5
Nezha Dashboard: DDNS and Notification credential exposure via unredacted list API
Summary
The GET /api/v1/ddns and GET /api/v1/notification endpoints return full resource objects including plaintext third-party API credentials — Cloudflare API tokens, TencentCloud SecretKeys, Slack/Discord/Telegram webhook URLs with embedded bot tokens, and Authorization header values — without any field-level redaction. Any authenticated admin who calls these endpoints receives every stored credential in the system in a single API response. A compromised admin session or leaked PAT with nezha:ddns:read or nezha:notification:read scope exposes all third-party integration secrets.
Details
The listDDNS and listNotification handlers follow an identical pattern: they call the corresponding singleton GetSortedList(), copier.Copy the full in-memory structs into a response slice, and return them via listHandler with zero field stripping.
DDNS — cmd/dashboard/controller/ddns.go:25–33:
func listDDNS(c *gin.Context) ([]*model.DDNSProfile, error) {
var ddnsProfiles []*model.DDNSProfile
list := singleton.DDNSShared.GetSortedList()
if err := copier.Copy(&ddnsProfiles, &list); err != nil {
return nil, err
}
return ddnsProfiles, nil
}
The DDNSProfile struct (model/ddns.go:20–36) serializes AccessSecret with json:"access_secret,omitempty" — non-empty Cloudflare tokens and TencentCloud SecretKeys are returned in cleartext. The WebhookURL and WebhookHeaders fields may also contain embedded secrets.
Notification — cmd/dashboard/controller/notification.go:25–33:
func listNotification(c *gin.Context) ([]*model.Notification, error) {
slist := singleton.NotificationShared.GetSortedList()
var notifications []*model.Notification
if err := copier.Copy(¬ifications, &slist); err != nil {
return nil, err
}
return notifications, nil
}
The Notification struct (model/notification.go:34–44) serializes URL, RequestHeader, and RequestBody — all of which commonly contain embedded bot tokens (Slack, Discord, Telegram), API keys in Authorization headers, and webhook secrets.
Route and authorization (cmd/dashboard/controller/controller.go:155, 171):
auth.GET("/notification", restScopeMiddleware(model.ScopeNotificationRead), listHandler(listNotification))
auth.GET("/ddns", restScopeMiddleware(model.ScopeDDNSRead), listHandler(listDDNS))
Both routes are behind authMw (JWT or PAT) and the corresponding read scope. The listHandler → filter chain uses HasPermission (model/common.go:63–82) which grants admins access to ALL profiles and restricts members to their own. No separate response struct or field masking exists anywhere in the codebase — confirmed by exhaustive search for DDNSResponse, DDNSView, NotificationResponse, NotificationView, or any JSON middleware that strips sensitive fields.
The codebase already demonstrates awareness of this pattern: serverConfigSensitiveScope() in cmd/dashboard/controller/api_token_scope.go:117 was introduced to restrict client_secret exposure via GET /server/config/:id, tightening the scope from ScopeServerRead to ScopeServerWrite. No equivalent protection exists for the DDNS or Notification list endpoints.
Tested at commit 3d74cd94 (master, post v2.2.3). The vulnerable pattern has existed since the DDNS and notification list endpoints were introduced.
PoC
-
Deploy nezha with at least one admin user. Configure a DDNS profile with a Cloudflare API token (AccessSecret) and a Notification webhook pointing to a Slack incoming webhook URL (
https://hooks.slack.com/services/T.../B.../xxx...). -
Authenticate as the admin user. Call:
# DDNS credentials exposed curl -s -H "Authorization: Bearer <admin_jwt>" \ https://dashboard.example.com/api/v1/ddns \ | jq '.data[].access_secret' # Notification webhook secrets exposed curl -s -H "Authorization: Bearer <admin_jwt>" \…
- Record
GHSA-ww5p-j6cj-6mqq- CVSS 4.0
- 5.5 Medium
CVSS:4.0/AV:N/AC:L/AT:N/PR:H/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:P - Weakness
- CWE-200
- Updated
- 2026-11-02
OSVGO-2026-5832 No score
Nezha Dashboard: DDNS and Notification credential exposure via unredacted list API in github.com/nezhahq/nezha
Nezha Dashboard: DDNS and Notification credential exposure via unredacted list API in github.com/nezhahq/nezha.
NOTE: The source advisory for this report contains additional versions that could not be automatically mapped to standard Go module versions.
(If this is causing false-positive reports from vulnerability scanners, please suggest an edit to the report.)
The additional affected modules and versions are: github.com/nezhahq/nezha before v2.2.5.
- Record
GO-2026-5832- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
github.com/nezhahq/nezhaGo |
before 2.2.5 | 2.2.5 |
github.com/nezhahq/nezhaGo |
All versions | None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-59155 is a medium vulnerability (CVSS 6.9) in Nezhahq Nezha, published June 26, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.39%, the 32nd percentile. Fixed in github.com/nezhahq/nezha 2.2.5.
References
github.com/nezhahq/nezha/security/advisories/GHSA-ww5p-j6cj-6mqq
github.com/nezhahq/nezha/commit/39d398066d8c644fe452f74704e34ada6c7ab61e
github.com/nezhahq/nezha/releases/tag/v2.2.5
github.com/advisories/GHSA-ww5p-j6cj-6mqq
github.com/CVEProject/cvelistV5/tree/main/cves/2026/59xxx/CVE-2026-59155.json
nvd.nist.gov/vuln/detail/CVE-2026-59155
github.com/nezhahq/nezha
Left out here: the rest of 2 source descriptions. The JSON record has them.