CVE-2026-59092: Verified Repro With Script Download
CVE-2026-59092: JuiceFS through 1.3.1 exposes debug/metrics endpoints via shared http.DefaultServeMux, enabling authentication bypass and leakage of sensitive metadata connection strings, with potential DoS via profiling handlers.
CVE-2026-59092 is verified against JuiceFS (juicedata/juicefs) · go. Affected versions: JuiceFS <= 1.3.1. Fixed in commit a46979cdd4082217081ee99b931ddc53d038e47a. Vulnerability class: DoS. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00220.
What Is CVE-2026-59092?
CVE-2026-59092 is a high-severity authentication bypass in JuiceFS (through 1.3.1) where debug and metrics HTTP handlers registered on the shared http.DefaultServeMux are reachable without authentication, exposing /debug/pprof/* endpoints. Pruva reproduced it (reproduction REPRO-2026-00220).
CVE-2026-59092 Severity & CVSS Score
CVE-2026-59092 is rated high severity, with a CVSS base score of 7.0 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
Affected JuiceFS (juicedata/juicefs) Versions
JuiceFS (juicedata/juicefs) · go versions JuiceFS <= 1.3.1 are affected.
How to Reproduce CVE-2026-59092
pruva-verify REPRO-2026-00220 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00220/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-59092
- reached the target end-to-end
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
unauthenticated HTTP GET request to /debug/pprof/cmdline on JuiceFS metrics port
- HTTP GET to metrics port (0.0.0.0:9567) /debug/pprof/cmdline — no authentication required
Distinct same-root-cause variant of CVE-2026-59092: after the fix (commit a46979cd, PR #7214) that isolated the metrics/WebDAV/sync HTTP servers from http.DefaultServeMux, the debug agent in cmd/main.go:336 (and its twin in sdk/java/libjfs/main.go:573) still calls http.ListenAndServe(addr, nil) and still serves Defaul…
How the agent worked
Root Cause and Exploit Chain for CVE-2026-59092
JuiceFS through 1.3.1 exposes debug and metrics HTTP endpoints via the shared http.DefaultServeMux, enabling unauthenticated remote attackers to access sensitive /debug/pprof/* handlers. The /debug/pprof/cmdline endpoint leaks the full process command line, which includes metadata engine connection strings containing database credentials (e.g., Redis passwords), granting full read/write access to filesystem metadata. Other pprof handlers (/debug/pprof/heap, /debug/pprof/goroutine, /debug/pprof/profile) leak internal runtime state and can be abused for denial-of-service via CPU profiling.
- Package/component affected:
cmd/mount.go(exposeMetricsfunction),pkg/fs/http.go(StartHTTPServerfor WebDAV),pkg/sync/cluster.go(startManager) - Affected versions: JuiceFS through 1.3.1 (fixed in commit
a46979cdd4082217081ee99b931ddc53d038e47a) - Risk level: High (CVSS 7.7) — unauthenticated credential disclosure leading to metadata engine compromise and potential DoS
Impact Parity
- Disclosed/claimed maximum impact: Authentication bypass; disclosure of metadata engine connection strings with DB credentials via
/debug/pprof/cmdline; access to internal state via other pprof handlers; potential DoS via profiling endpoints - Reproduced impact from this run:
- Unauthenticated access to
/debug/pprof/cmdlinereturns HTTP 200 with full command line including Redis passwords3cr3tPassfrom the metadata URLredis://:s3cr3tPass@127.0.0.1:6379/1 - All pprof endpoints (
/debug/pprof/,/debug/pprof/heap,/debug/pprof/goroutine,/debug/pprof/profile) return HTTP 200 without authentication - Fixed version returns HTTP 404 for all pprof endpoints while
/metricsstill works
- Unauthenticated access to
- Parity:
full— the authentication bypass and credential leakage are fully demonstrated through the real product (JuiceFS gateway) via the remote HTTP API surface - Not demonstrated: Full metadata compromise (using the leaked credentials to access the Redis metadata engine) and DoS via profiling — these are downstream consequences of the credential leak, not separate reproduction targets
Root Cause
The exposeMetrics() function in cmd/mount.go calls http.Handle("/metrics", ...) which registers the metrics handler on the shared http.DefaultServeMux. It then starts the HTTP server with http.Serve(ln, nil), where the nil handler defaults to http.DefaultServeMux.
Since the net/http/pprof package is imported via _ "net/http/pprof" in multiple files (cmd/main.go, cmd/mount.go, cmd/gateway.go, cmd/format.go, cmd/sync.go), its init() function registers pprof handlers (/debug/pprof/cmdline, /debug/pprof/heap, /debug/pprof/goroutine, /debug/pprof/profile, etc.) on http.DefaultServeMux at program startup. As a result, all pprof endpoints are served without authentication alongside the metrics endpoint on whatever address the metrics server binds to (default 127.0.0.1:9567, but configurable to 0.0.0.0:9567 for remote access).
The same pattern affects:
pkg/fs/http.goStartHTTPServer()— WebDAV server useshttp.ListenAndServe(addr, nil)pkg/sync/cluster.gostartManager()— sync manager useshttp.Serve(l, nil)
Fix commit: a46979cdd4082217081ee99b931ddc53d038e47a ("cmd: use a dedicated ServeMux to avoid exposing pprof/metrics")
The fix creates a dedicated http.NewServeMux() for each HTTP server and passes it to http.Serve(ln, mux) instead of nil, isolating the application handlers from the pprof handlers registered on DefaultServeMux.
Vulnerable code (cmd/mount.go, commit f60a90fc):
http.Handle("/metrics", promhttp.HandlerFor(...)) // registers on DefaultServeMux
// ...
http.Serve(ln, nil) // nil → uses DefaultServeMux → exposes pprof
Fixed code (cmd/mount.go, commit a46979cd):
mux := http.NewServeMux() // dedicated mux
mux.Handle("/metrics", promhttp.HandlerFor(...)) // registers on dedicated mux
// ...
http.Serve(ln, mux) // uses dedicated mux → no pprof exposure
Reproduction Steps
- Reference:
bundle/repro/reproduction_steps.sh - What the script does:
- Installs Go 1.25.0 and Redis if not already available
- Clones the JuiceFS repository (or reuses the project cache)
- Builds the vulnerable binary at commit
f60a90fc(parent of the fix) - Builds the fixed binary at commit
a46979cd - Starts Redis with password
s3cr3tPassto simulate a credential-bearing metadata engine - Formats a JuiceFS volume using
redis://:s3cr3tPass@127.0.0.1:6379/1as the metadata URL - Starts the JuiceFS S3 gateway with
--metrics 0.0.0.0:9567(remotely accessible metrics port) - Sends unauthenticated HTTP GET to
/debug/pprof/cmdlineon the metrics port - Verifies the response contains the Redis password (vulnerable version)
- Repeats with the fixed binary and verifies HTTP 404 (no pprof exposure)
- Expected evidence of reproduction:
- Vulnerable version: HTTP 200 for
/debug/pprof/cmdlinewith response body containingredis://:s3cr3tPass@127.0.0.1:6379/1 - Fixed version: HTTP 404 for
/debug/pprof/cmdline - Both versions: HTTP 200 for
/metrics(metrics endpoint still functional after fix)
- Vulnerable version: HTTP 200 for
Evidence
Log files
bundle/logs/gateway-vuln.log— vulnerable gateway startup and metrics listening logbundle/logs/gateway-fixed.log— fixed gateway startup and metrics listening logbundle/logs/redis.log— Redis server logbundle/logs/format.log— JuiceFS volume format log
Response artifacts
bundle/repro/artifacts/vuln-cmdline-response.txt— raw response from vulnerable/debug/pprof/cmdlinebundle/repro/artifacts/fixed-cmdline-response.txt— raw response from fixed/debug/pprof/cmdlinebundle/repro/artifacts/vuln-metrics-response.txt— Prometheus metrics from vulnerable version
Key excerpts
Vulnerable /debug/pprof/cmdline response (HTTP 200):
/data/pruva/project-cache/.../juicefs-vuln
gateway
redis://:s3cr3tPass@127.0.0.1:6379/1
localhost:9000
--metrics
0.0.0.0:9567
--no-banner
Fixed /debug/pprof/cmdline response (HTTP 404):
404 page not found
Vulnerable pprof endpoints (all HTTP 200 without auth):
/debug/pprof/: HTTP 200
/debug/pprof/heap: HTTP 200
/debug/pprof/goroutine: HTTP 200
/debug/pprof/profile: HTTP 200
/metrics: HTTP 200
Fixed pprof endpoints (all HTTP 404):
/debug/pprof/: HTTP 404
/debug/pprof/heap: HTTP 404
/debug/pprof/goroutine: HTTP 404
/debug/pprof/profile: HTTP 404
/metrics: HTTP 200
Environment details
- Go version: go1.25.0 linux/amd64
- Redis version: 8.0.5
- JuiceFS vulnerable commit:
f60a90fc0ad52d2bb1f44f38a04d55044fc91d50 - JuiceFS fixed commit:
a46979cdd4082217081ee99b931ddc53d038e47a - Architecture: x86_64
Recommendations / Next Steps
- Upgrade: Update to a JuiceFS version containing commit
a46979cdor later - Network restriction: Bind the metrics port to localhost (
127.0.0.1:9567) rather than0.0.0.0:9567unless remote monitoring is explicitly required - Debug agent: Use the
--no-agentflag to disable the debug agent on port 6060, which still useshttp.ListenAndServe(debugAgent, nil)withDefaultServeMuxeven after the fix (though it binds to localhost only) - Firewall: Restrict network access to the metrics and debug ports using firewall rules
- Credential rotation: If credentials were exposed via this vulnerability, rotate all metadata engine passwords immediately
Additional Notes
- Idempotency: The script was run twice consecutively, both times confirming the vulnerability with identical results (exit code 0)
- Scope: The fix addresses
exposeMetrics(mount/gateway/sync/mdtest),StartHTTPServer(WebDAV), andstartManager(sync cluster). The debug agent incmd/main.goline 336 (http.ListenAndServe(debugAgent, nil)) remains unchanged but is bound to127.0.0.1only, limiting it to local access - Negative control: The fixed version binary was built from the exact fix commit and demonstrates that pprof endpoints return 404 while
/metricscontinues to function, proving the fix is surgical and does not break metrics collection
Variant Analysis & Alternative Triggers for CVE-2026-59092
The official fix for CVE-2026-59092 (commit a46979cdd4082217081ee99b931ddc53d038e47a, PR #7214) correctly isolated the metrics, WebDAV, and sync-cluster HTTP servers from http.DefaultServeMux — closing the remote pprof exposure on operator-bindable ports. However, the fix did not touch the debug agent started in cmd/main.go:336 (http.ListenAndServe(debugAgent, nil)), nor its twin in the Java SDK (sdk/java/libjfs/main.go:573). Because _ "net/http/pprof" is imported, both debug agents still serve the shared DefaultServeMux and still expose /debug/pprof/cmdline, which leaks the full process command line — including the metadata-engine URL with database credentials. This is a distinct variant of the same root cause (DefaultServeMux + pprof import + nil handler) via a different entry point (the debug agent port 127.0.0.1:6060 instead of the metrics port) that the fix did not cover. It is confirmed to reproduce on the fixed binary. Because the debug agent is hardcoded to 127.0.0.1 (no flag rebinds it to 0.0.0.0), this is not a remote bypass of the original CVE; it is a localhost-only residual / fix-coverage gap reachable by a co-located local user or an SSRF from a co-located service, and disableable via --no-agent.
Fix Coverage / Assumptions
- Invariant the fix relies on: "Every remotely-reachable JuiceFS HTTP server that previously served
DefaultServeMuxis one ofexposeMetrics(cmd/mount.go),StartHTTPServer(pkg/fs/http.go), orstartManager(pkg/sync/cluster.go)." - Code paths explicitly covered: the metrics port (mount/gateway/sync/mdtest), the WebDAV port, and the sync-cluster manager port. Each now uses a dedicated
http.NewServeMux()passed as the handler instead ofnil. - What the fix does NOT cover: the
nil-handler debug agent incmd/main.go:336(started for every subcommand unless--no-agent) and the Java SDK debug agent insdk/java/libjfs/main.go:573.git show <fix> -- cmd/main.gois an empty diff, proving the debug agent was untouched. Both still import_ "net/http/pprof"and still serveDefaultServeMux.
Variant / Alternate Trigger
Entry point: an unauthenticated HTTP
GETto/debug/pprof/cmdlineon the debug agent port127.0.0.1:6060(first free port in6060..6099) of a running JuiceFS process — not the metrics port used by the original CVE.Code path:
cmd/main.go:332-337→http.ListenAndServe("127.0.0.1:6060", nil)→DefaultServeMux→net/http/pprof/debug/pprof/cmdlinehandler →runtimeprocess command line (which contains theredis://:s3cr3tPass@127.0.0.1:6379/1metadata URL passed on thejuicefs gateway/mountcommand line).Secondary anchor (same root cause, same sink):
sdk/java/libjfs/main.go:573—http.ListenAndServe(fmt.Sprintf("127.0.0.1:%d", port), nil), gated byjConf.Debug || JUICEFS_DEBUG.Why it bypasses the fix's coverage: the fix only converted the three remote-facing servers to dedicated muxes; the debug agent was left on
DefaultServeMux, so the identical pprof sink is still reachable on the fixed commit.Package/component affected:
cmd/main.go(debug agent,debugAgentOncegoroutine), andsdk/java/libjfs/main.go(Java SDK debug agent). Sink:net/http/pprof/debug/pprof/cmdline(and sibling handlersheap,goroutine,allocs,threadcreate,block,profile).Affected versions (as tested): confirmed present on the fixed commit
a46979cdd4082217081ee99b931ddc53d038e47a(and on the vulnerable parentf60a90fc— behavior is unchanged by the fix). Affects all JuiceFS ≤ 1.3.1 and the fixed commit, for any process started without--no-agent.Risk level: Medium (local) — not a remote bypass. On a shared/multi-tenant host, a co-located unprivileged user or an SSRF from a co-located web service can read the operator's metadata-engine credentials from a fixed JuiceFS process; the other pprof handlers leak internal runtime state and
profileenables a local CPU-DoS. The original CVE's remote auth-bypass on the metrics port is not reproduced (metrics port returns 404 on the fixed binary).
Impact Parity
- Disclosed/claimed maximum impact (parent CVE): unauthenticated remote authentication bypass and disclosure of metadata-engine connection strings with DB credentials via
/debug/pprof/cmdline; internal-state leakage and DoS via other pprof handlers. - Reproduced impact from this variant run (on the fixed binary):
/debug/pprof/cmdlineon127.0.0.1:6060→ HTTP 200, response body containsredis://:s3cr3tPass@127.0.0.1:6379/1(credential leak confirmed).- Other pprof handlers on the debug agent return HTTP 200 (
heap,goroutine,allocs,threadcreate,block;/debug/pprof/→ 301;profileblocks for its profiling duration = DoS surface). - Metrics port
/debug/pprof/cmdline→ HTTP 404 (fix holds on the remote surface);/metrics→ HTTP 200 (no regression). - Debug agent reachable from a non-loopback host IP (
172.20.0.11:6060) → connection refused (loopback_only). - With
--no-agent, the debug agent is not listening (mitigation works).
- Parity: partial — the same sink and the same credential-leak impact are reproduced, but on a localhost-only surface, not the remote surface of the parent CVE. Remote auth-bypass is not reproduced (the fix closes it).
- Not demonstrated: remote exploitation; cross-host credential theft. The demonstrated primitive is local/loopback credential disclosure on the fixed version.
Root Cause
The root cause is identical to the parent CVE: a Go HTTP server is started with a nil handler (which defaults to the process-wide http.DefaultServeMux) while _ "net/http/pprof" is imported, so the pprof handlers registered on DefaultServeMux become reachable without authentication. The parent CVE's instance was the metrics port (http.Serve(ln, nil) in exposeMetrics); this variant's instance is the debug agent (http.ListenAndServe(debugAgent, nil) in cmd/main.go:336). The fix eliminated the nil-handler usage in the three remote-facing servers but left the debug agent's nil-handler usage intact, so the same underlying bug is still reachable via the debug agent entry point on the fixed commit.
- Fix commit:
a46979cdd4082217081ee99b931ddc53d038e47a(PR #7214).
Reproduction Steps
- Reference:
bundle/vuln_variant/reproduction_steps.sh - What the script does (idempotent, tests both vulnerable and fixed binaries side by side):
- Reuses the prebuilt
juicefs-vuln(commitf60a90fc) andjuicefs-fixed(commita46979cd) binaries and a password-protected Redis (s3cr3tPass) from the project cache. - Formats a JuiceFS volume with
redis://:s3cr3tPass@127.0.0.1:6379/1. - TEST 1 (vulnerable): starts
juicefs-vuln gateway(debug agent enabled) → checks metrics-port pprof (baseline, expect 200) and scans127.0.0.1:6060..6099for the debug agent → curls/debug/pprof/cmdlineand checks fors3cr3tPass. - TEST 2 (fixed): starts
juicefs-fixed gateway(debug agent enabled) → checks metrics-port pprof (expect 404) and/metrics(expect 200) → finds the debug agent on127.0.0.1:6060→ curls/debug/pprof/cmdline(the variant) and checks fors3cr3tPass→ tests reachability from a non-loopback IP (expect refused) → enumerates other pprof handlers. - TEST 3 (mitigation): starts
juicefs-fixed gateway --no-agent→ confirms the debug agent is no longer listening. - Writes
bundle/vuln_variant/variant_runtime_result.jsonand exits 0 if the fixed debug agent leaks credentials (variant confirmed on fixed version), else 1.
- Reuses the prebuilt
- Expected evidence of reproduction: fixed debug agent
/debug/pprof/cmdline→ HTTP 200 with body containingredis://:s3cr3tPass@127.0.0.1:6379/1; fixed metrics port pprof → 404; non-loopback → refused;--no-agent→ agent disabled.
Evidence
- Run logs:
bundle/logs/vuln_variant/variant-run-{4,5,6,7}.log(runs 6 & 7 are the verified idempotent pair; both exit 0 with identical results). - Runtime evidence:
bundle/vuln_variant/variant_runtime_result.json—variant_on_fixed=true,fixed_debug_agent_leak=true,fixed_debug_port=6060,fixed_metrics_pprof=404,fixed_metrics_ok=200,fixed_debug_reachability=loopback_only,fixed_noagent_result=disabled. - Source identity:
bundle/logs/vuln_variant/fixed_version.txt—fixed_git_rev_parse=a46979cdd4082217081ee99b931ddc53d038e47a. - Captured responses (raw, NUL→newline):
bundle/logs/vuln_variant/fixed-debugagent-cmdline.txt— fixed debug agent/debug/pprof/cmdlinebody:/data/pruva/project-cache/.../juicefs-fixed gateway redis://:s3cr3tPass@127.0.0.1:6379/1 <-- credential leaked on FIXED binary localhost:9101 --metrics 0.0.0.0:9578 --no-bannerbundle/logs/vuln_variant/vuln-debugagent-cmdline.txt— vulnerable debug agent (identical leak).
- Gateway logs:
bundle/logs/vuln_variant/gateway-{vuln,fixed,fixed-noagent}.log. - Endpoint enumeration (fixed debug agent, run #6):
/debug/pprof/ : HTTP 301 /debug/pprof/heap : HTTP 200 /debug/pprof/goroutine: HTTP 200 /debug/pprof/profile : HTTP 000 (blocks for profiling duration -> DoS surface) /debug/pprof/allocs : HTTP 200 /debug/pprof/threadcreate: HTTP 200 /debug/pprof/block : HTTP 200 - Environment: Go 1.25.0 linux/amd64, Redis 8.0.5, x86_64; vulnerable commit
f60a90fc0ad52d2bb1f44f38a04d55044fc91d50; fixed commita46979cdd4082217081ee99b931ddc53d038e47a(verifiedgit rev-parse HEAD).
Recommendations / Next Steps
- Extend the fix to the debug agent (
cmd/main.go:332-337): serve a dedicated mux (or register pprof on a separate mux that the debug agent's listener serves) so/debug/pprof/*is not exposed viaDefaultServeMuxeven on localhost. Apply the same tosdk/java/libjfs/main.go:569-574. - Defense in depth: keep the
127.0.0.1binding and document that--no-agent(orJUICEFS_DEBUGunset for the Java SDK) is required on shared/multi-tenant hosts, since a co-located user or SSRF can otherwise extract metadata credentials from a fixed deployment. - Optionally add a regression test mirroring
cmd/mount_test.go/pkg/sync/cluster_test.gothat asserts the debug agent does not leakos.Argsvia/debug/pprof/cmdlinewhen pprof is isolated. - Do not treat this as a re-opening of the remote metrics-port surface — that is correctly closed; this is a local-hardening gap in the same root-cause family.
Additional Notes
- Idempotency: the script was run multiple times; runs #6 and #7 (the verified pair) both completed with exit 0 and identical summaries, with no lingering processes. Earlier port-race failures were fixed by adding a port-free wait + global cleanup; cleanup is targeted to the exact built binary paths to avoid self-kill.
- Trust boundary: the original CVE is remote (operator binds metrics to
0.0.0.0); this variant is localhost (debug agent hardcoded to127.0.0.1, no remote-bind flag). It crosses a local user-to-user/process-to-process boundary, not the parent's remote boundary — hence "partial" parity and "local" risk, not a remote bypass. - Bounded search: a full scan of the fixed commit found exactly two remaining
nil-handlerDefaultServeMuxservers (the two debug agents). All other HTTP servers (metrics/WebDAV/sync) were converted by the fix; the gateway's S3 port is served by minio's own router, notDefaultServeMux. No additional materially distinct remote entry points exist.
CVE-2026-59092 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Artifacts and Evidence for CVE-2026-59092
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-59092
Upgrade JuiceFS (juicedata/juicefs) · go to commit a46979cdd4082217081ee99b931ddc53d038e47a or later.
FAQ: CVE-2026-59092
How does the CVE-2026-59092 credential-leak attack work?
/debug/pprof/cmdline returns the full process command line, which includes the metadata engine connection string with embedded database credentials (e.g. a Redis URL such as redis://:<password>@127.0.0.1:6379/1); those credentials grant full read/write access to filesystem metadata, and the other pprof handlers leak internal runtime state or can be abused for CPU-profiling denial-of-service.Which JuiceFS versions are affected by CVE-2026-59092, and where is it fixed?
a46979cdd4082217081ee99b931ddc53d038e47a.How severe is CVE-2026-59092?
How can I reproduce CVE-2026-59092?
/debug/pprof/cmdline unauthenticated and confirms the metadata engine's connection string, including credentials, is disclosed in the response.References for CVE-2026-59092
Authoritative sources for CVE-2026-59092 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.