GitHub Advisoryadvisory · Go
GHSA-x92x-px7w-4gx4 Dgraph: Pre-Auth Full Database Exfiltration via DQL Injection in NQuad Lang Field
CRITICAL
Official page ↗
- Aliases
- CVE-2026-41328
- Ecosystem
- Go
- Packages
- github.com/dgraph-io/dgraph/v25, github.com/dgraph-io/dgraph/v24, github.com/dgraph-io/dgraph
- Severity
- Critical
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
- Weaknesses
- CWE-943
- Reviewed
- 2026-04-24
Affected packages
• Go github.com/dgraph-io/dgraph/v25: < 25.3.3 → fixed in 25.3.3
• Go github.com/dgraph-io/dgraph/v24: <= 24.1.8 (no fix yet)
• Go github.com/dgraph-io/dgraph: <= 1.2.8 (no fix yet)
Description
1. Executive Summary
A vulnerability has been found in Dgraph that gives an unauthenticated attacker full read access to every piece of data in the database. This affects Dgraph's default configuration where ACL is not enabled.
The attack requires two HTTP POSTs to port 8080. The first sets up a schema predicate with `@unique @index(exact) @lang` via `/alter` (also unauthenticated in default config). The second sends a crafted JSON mutation to `/mutate?commitNow=true` where a JSON key contains the predicate name followed by `@` and a DQL injection payload in the language tag position.
The injection exploits the `addQueryIfUnique` function in `edgraph/server.go`, which constructs DQL queries using `fmt.Sprintf` with unsanitized `predicateName` that includes the raw `pred.Lang` value. The `Lang` field is extracted from JSON mutation keys by `x.PredicateLang()`, which splits on `@`, and is never validated by any function in the codebase. The attacker injects a closing parenthesis to escape the `eq()` function, adds an arbitrary named query block, and uses a `#` comment to neutralize trailing template syntax. The injected query executes server-side and its results are returned in the HTTP response.
POC clip:
https://github.com/user-attachments/assets/bbfb7bba-c957-4b57-b534-48a958314186
2. CVSS Score
CVSS 3.1: 9.1 (Critical)
```
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
```
| Metric | Value | Rationale |
| ------------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------- |
| Attack Vector | Network | HTTP POST to port 8080 |
| Attack Complexity | Low | Two requests, deterministic outcome, no special conditions |
| Privileges Required | None | No authentication when ACL is disabled (default) |
| User Interaction | None | Fully automated |
| Scope | Unchanged | Stays within the Dgraph data layer |
| Confidentiality | High | Full database exfiltration: all nodes, all predicates, all values |
| Integrity | High | The mutation that carries the injection also writes data; the attacker can also set up arbitrary schema via unauthenticated /alter |
| Availability | None | No denial of service |
3. Vulnerability Summary
| Field | Value |
| ----- | --------------------------------------------------------------------------- |
| Title | Pre-Auth DQL Injection via Unsanitized NQuad Lang Field in addQueryIfUnique |
| Type | Injection |
| CWE | CWE-943 (Improper Neutralization of Special Elements in Data Query Logic) |
| CVSS | 9.8 |
4. Target Information
| Field | Value |
| --------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| Project | Dgraph |
| Repository | https://github.com/dgraph-io/dgraph |
| Tested version | v25.3.0 |
| Lang split | `x/x.go` line 919 (`PredicateLang` splits on `@`, returns everything after as `Lang`) |
| Lang assignment | `chunker/json_parser.go` line 524 (`nq.Predicate, nq.Lang = x.PredicateLang(nq.Predicate)`) |
| Validation gap | `edgraph/server.go` line 2142 (`validateKeys` checks `nq.Predicate` only, never `nq.Lang`) |
| Injection sink | `edgraph/server.go` line 1808 (`fmt.Sprintf` with `predicateName` containing raw `pred.Lang`) |
| predicateName build | `edgraph/server.go` line 1780 (`fmt.Sprintf("%v@%v", predicateName, pred.Lang)`) |
| Auth bypass (query) | `edgraph/access.go` line 958 (`authorizeQuery` returns nil when `AclSecretKey == nil`) |
| Auth bypass (mutate) | `edgraph/access.go` line 788 (`authorizeMutation` returns nil when `AclSecretKey == nil`) |
| Response exfiltration | `dgraph/cmd/alpha/http.go` line 498 (`mp["queries"] = json.RawMessage(resp.Json)`) |
| HTTP port | 8080 (default) |
| Prerequisite | A predicate with `@unique @index(exact) @lang` in the schema. The attacker can create this via unauthenticated `/alter`. |
5. Test Environment
| Component | Version / Details |
| -------------- | ------------------------------------------------------------------ |
| Host OS | macOS (darwin 25.3.0) |
| Dgraph | v25.3.0 via `dgraph/dgraph:latest` Docker image |
| Docker Compose | 1 Zero + 1 Alpha, default config, `whitelist=0.0.0.0/0` |
| Python | 3.x with `requests` |
| Network | localhost (127.0.0.1) |
6. Vulnerability Detail
Location: `edgraph/server.go` lines 1778-1808 (`addQueryIfUnique`)
CWE: CWE-943 (Improper Neutralization of Special Elements in Data Query Logic)
The `/mutate` endpoint accepts JSON mutations. When a predicate has the `@unique` directive, the `addQueryIfUnique` function builds a DQL query to check whether the value already exists.
The JSON chunker at `json_parser.go:524` splits mutation keys on `@` via `x.PredicateLang`:
```go
nq.Predicate, nq.Lang = x.PredicateLang(nq.Predicate)
```
`PredicateLang` at `x/x.go:919` splits on the last `@` and returns everything after it as the `Lang` string with no validation:
```go
func PredicateLang(s string) (string, string) {
i := strings.LastIndex(s, "@")
if i ", pred.Predicate)
if pred.Lan…
Related
Alias · CVE (1)
- CVE-2026-41328 Dgraph is an open source distributed GraphQL database. vulnerability
Weakness · CWE (1)
- CWE-943 Improper Neutralization of Special Elements in Data Query Logic weakness