
Remotecodeausführung in DbGate durch functionName-Injection im loadReader-Endpunkt — CVSS 8.8
functionName-InjektionSchweregrad: Hoch (CVSS 8.8)
CWE: CWE-94 — Unzureichende Kontrolle der Codegenerierung ('Code-Injektion')
Betroffen: dbgate-api ≤ 7.1.8 (behoben in 7.1.9)
Advisory: GHSA-hv83-ggc4-v385
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-48017
Autor: Romain Deperne
Der POST /runners/load-reader-Endpunkt nimmt einen functionName-Parameter entgegen und interpoliert ihn direkt in eine JavaScript-Vorlagenzeichenfolge, die dann in einem forked-Prozess ausgeführt wird – ohne Bereinigung oder Validierung. Jeder authentifizierte Benutzer (keine speziellen Berechtigungen erforderlich) kann aus dem vorgesehenen Aufruf ausbrechen und beliebiges JavaScript ausführen, was zu einer Remotecodeausführung auf dem Server führt.
Der forked-Runner setzt als Sandbox, aber das lässt sich trivial umgehen: ist weiterhin erreichbar und erzeugt einen echten Betriebssystemprozess.
require = nullprocess.binding("spawn_sync")Ich habe das Runner-Subsystem von DbGate auf die Lücke zwischen bewachten und unbewachten Code-Ausführungspfaden untersucht. Der start()-Runner in runners.js:292 ist ordnungsgemäß abgesichert – er ruft testStandardPermission('run-shell-script') auf und prüft platformInfo.allowShellScripting.
loadReader() macht konzeptionell dasselbe (es erstellt und führt ein JS-Loader-Skript aus), hat aber keine dieser Prüfungen. Ich habe den Datenfluss nachverfolgt:
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 wird vom Angreifer kontrolliert und landet innerhalb einer ausgeführten Vorlagenzeichenfolge. Das Präfix dbgateApi. ist das Einzige davor und es ist escapebar: Durch Schließen des Ausdrucks mit toString();, Injizieren der Nutzlast und Auskommentieren des nachfolgenden (${props}) mit // wird gültiges JS erzeugt.
Datei: packages/api/src/controllers/runners.js (loadReader → loaderScriptTemplate)
Datei: 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)});`
Generiertes (injiziertes) Skript:
const reader = await dbgateApi.toString();
process.binding("spawn_sync").spawn({ file:"/bin/sh", args:["/bin/sh","-c","id"], ... });
dbgateApi.toString//({});
compileShellApiFunctionName() wurde geschrieben, um einen kurzen Namen in einen vollqualifizierten API-Pfad (dbgateApi.<name>) umzuwandeln, wobei implizit darauf vertraut wird, dass functionName ein Bezeichner ist. Der Wert kommt jedoch direkt aus dem HTTP-Anforderungstext. Da er in Quellcode eingefügt wird, der später eval/geforkt wird, führt die Vertrauensannahme zu einer RCE. Die require = null-Sandbox hilft nicht – Node's interner process.binding("spawn_sync") umgeht sie.
Behebung (7.1.9): Überprüfen Sie functionName anhand einer strengen Zulassungsliste für Bezeichner, bevor Sie es in das Loader-Skript kompilieren.
poc/rce_loadreader_functionname_injection.py – treibt den echten Endpunkt Ende-zu-Ende an.
# 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'
Das Skript erstellt die functionName-Nutzlast, sendet sie an POST /runners/load-reader, und der injizierte spawn_sync-Aufruf führt den Befehl im geforkten Runner-Prozess aus.
Authentifizierte RCE auf dem DbGate-API-Host. DbGate ist eine Datenbankverwaltungs-GUI, die häufig mit breitem Netzwerkzugriff auf interne Datenbanken bereitgestellt wird – eine Codeausführung darauf ist ein starker Hebel in die Datenebene.
Verantwortungsvoll offengelegt. PoC nach der Auslieferung des Fixes veröffentlicht, für Verteidiger und Erkennungstechnik.