Every CVE Search/ Get API key

CVE-2026-59155

Nezha Monitoring: DDNS and Notification credential exposure via unredacted list API

6.9 MediumFix available

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

0.39%Up 0.1 points since Jul 11, 2026
90%50%10%1%

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 MediumCVSS: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 MediumCVSS: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(&notifications, &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

  1. 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...).

  2. 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 MediumCVSS: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 MediumCVSS: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 MediumCVSS: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(&notifications, &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

  1. 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...).

  2. 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 MediumCVSS: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

Affected packages
PackageAffectedFixed
github.com/nezhahq/nezha
Go
before 2.2.5
2.2.5
github.com/nezhahq/nezha
Go
All versions None known

History

History
RecordedChangedSources
first indexedCVE 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

This page doesn't list everything

Left out here: the rest of 2 source descriptions. The JSON record has them.

MoveEnter Open