hexmortem · 0x00400420confidentialv24.04 · sha 9f4e…a2c1pgp 0x783E 8C5A
LIVE · incident intake operational pgp 5421 993B … EAB8 0385 lat EU-SW · 42ms tz UTC+01 · AD
uptime 99.98% queue 3 active · 2 pending last note · 2026-05-30
CVE BRIEF

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)

  1. GET /debug/vars — no auth, no headers. Returns ~5 KB JSON. The cmdline key contains os.Args.
  2. Parse the token from --security "token=<token>" in the published argv.
  3. Replay the token in the X-Dgraph-AuthToken header against POST /admin (GraphQL) or /admin/config/* (REST). Get 200 OK with 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 ruleWhat it sees
sigma-http-debug-vars.ymlInitial probe — GET /debug/vars from non-allowlisted source IPs
sigma-http-admin-replay.ymlToken replay — sequence of /debug/vars GET → /admin POST from the same source within 5 minutes
sigma-app-admin-graphql.ymlSuccessful 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

SurfaceVerdictAction
Alpha :8080 reachable, token via --security flagPWNEDRotate token, audit /admin access logs back ≥30 days, upgrade to v25.3.3
Alpha behind reverse proxy stripping /debug/*Not exploitableProxy posture mitigates — verify with the alpha-tail.sh probe
Token via DGRAPH_ALPHA_SECURITY env varNot exploitableexpvar doesn’t publish env; mitigated by deployment pattern
Alpha publicly exposed without --security at allN/A — different problemAdmin 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 in github.com/hexmortem/detection-rules. Confidential triage on this or related CVEs: scope@hexmortem.com.