CVE-2026-41492 in Dgraph: how the admin token leaks through `/debug/vars` and what survives the v25.3.3 patch
CVE-2026-41492
Verdict: exploitable
Affected
Dgraph Alpha ≤ v25.3.2 deployed with `--security "token=..."` and Alpha :8080 reachable from the attacker
One-line. Dgraph Alpha ≤ v25.3.2 publishes its admin token to anyone who can reach port 8080, because Go’s expvar package — pulled in transitively by the metrics path — auto-registers /debug/vars on the default HTTP mux and serializes os.Args. CVSS 9.8 / CWE-200 → auth bypass. Fixed in v25.3.3.
What we did in the lab. Built a v25.3.2 reproducer using the documented --security "token=..." flag, ran the three-step attack chain end-to-end, then ran the same script against a v25.3.3 build to confirm the patch holds. Captured transcripts, derived three Sigma rules, one ModSecurity recipe, two YARA rules, and an Express middleware allowlist for environments that reverse-proxy Alpha.
The attack chain (verified)
GET /debug/vars— no auth, no headers. Returns ~5 KB JSON. Thecmdlinekey containsos.Args.- Parse the token from
--security "token=<token>"in the published argv. - Replay the token in the
X-Dgraph-AuthTokenheader againstPOST /admin(GraphQL) or/admin/config/*(REST). Get200 OKwith admin payload.
This works on every v25.3.2 deployment that follows the documented token-via-CLI pattern. It does not work when the token is supplied via DGRAPH_ALPHA_SECURITY env var — expvar doesn’t publish env. That’s a meaningful triage signal for incident scope.
What public summaries get wrong
Several writeups frame this as "the CVE-2026-40173 fix didn’t work." That is wrong. The prior fix worked exactly as written:
// v25.3.2 dgraph/cmd/alpha/run.go
if r.URL.Path == "/debug/pprof/cmdline" {
http.NotFound(w, r)
return
}
http.DefaultServeMux.ServeHTTP(w, r)
/debug/pprof/cmdline correctly returns 404 on v25.3.2. The new CVE is a different endpoint on the same default mux (/debug/vars, registered by expvar.init). The lesson — applicable far beyond Dgraph — is that path-allowlisting cannot patch a class of bugs whose root cause is "the default mux exposes anything a transitive import registered." The v25.3.3 patch acknowledges this: it routes /debug/vars through a filtering handler (x.SanitizedDefaultServeMux(), x/server.go) that strips cmdline rather than trying to enumerate every dangerous endpoint.
Detection — three Sigma rules, two YARA, one ModSecurity recipe
We shipped three Sigma rules covering the three independent observable signals:
| Sigma rule | What it sees |
|---|---|
sigma-http-debug-vars.yml | Initial probe — GET /debug/vars from non-allowlisted source IPs |
sigma-http-admin-replay.yml | Token replay — sequence of /debug/vars GET → /admin POST from the same source within 5 minutes |
sigma-app-admin-graphql.yml | Successful 200 OK admin GraphQL with X-Dgraph-AuthToken from a non-prod source |
The replay rule is the high-fidelity signal: /debug/vars alone gets hit by every Prometheus-style scraper; the temporal join with a successful admin call is what makes the alert ownable.
YARA rules cover the request- and response-body patterns for off-line PCAP review (we use them to retro-classify NetFlow archives during incident scoping).
For organisations that fronts Alpha with a reverse proxy, the dgraph-admin-allowlist.express.js middleware is the lift-and-shift mitigation: deny /debug/* outright, allow /admin only from the bastion CIDR.
Triage decision tree
| Surface | Verdict | Action |
|---|---|---|
Alpha :8080 reachable, token via --security flag | PWNED | Rotate token, audit /admin access logs back ≥30 days, upgrade to v25.3.3 |
Alpha behind reverse proxy stripping /debug/* | Not exploitable | Proxy posture mitigates — verify with the alpha-tail.sh probe |
Token via DGRAPH_ALPHA_SECURITY env var | Not exploitable | expvar doesn’t publish env; mitigated by deployment pattern |
Alpha publicly exposed without --security at all | N/A — different problem | Admin endpoints are open by default in Dgraph; this is a config issue, not this CVE |
What this means for your stack
If you run Dgraph: version-audit.sh (in the detections bundle) crawls a list of host:port targets and reports v25.3.2 instances with reachable /debug/vars. Pipe in your asset inventory; the script returns PWNED / PATCHED / UNREACHABLE per host. Don’t grep response codes — the rule of thumb is "if cmdline is in the JSON response body, you’re vulnerable, regardless of status."
If you don’t run Dgraph but you write Go services: expvar is a transitive-import landmine. Audit http.DefaultServeMux use in production binaries. The same bug exists wherever a security-sensitive arg is passed via CLI flag in a Go program that exposes any HTTP listener and imports any package that touches expvar (directly or via pprof, which inits expvar).
Hexmortem Labs published this brief on 2026-04-26 as part of our public defender briefs. The reproducer lab, captured transcripts, full risk-assessment walkthrough, and detection bundle are at
github.com/hexmortem/research-labs/tree/main/cve-2026-41492. The detections also live as standalone YARA/Sigma drops ingithub.com/hexmortem/detection-rules. Confidential triage on this or related CVEs:scope@hexmortem.com.