
Esecuzione di codice in remoto in DbGate tramite injection di functionName nell'endpoint loadReader — CVSS 8.8
functionNameGravità: Alta (CVSS 8.8)
CWE: CWE-94 — Controllo improprio della generazione di codice ('Code Injection')
Versioni interessate: dbgate-api ≤ 7.1.8 (corretta in 7.1.9)
Advisory: GHSA-hv83-ggc4-v385
NVD: https://nvd.nist.gov/vuln/detail/CVE-2026-48017
Segnalazione: Romain Deperne
L'endpoint POST /runners/load-reader accetta un parametro functionName e lo interpola
direttamente in una stringa template JavaScript che viene poi eseguita in un processo fork — senza
sanificazione né validazione. Qualsiasi utente autenticato (senza bisogno di permessi speciali) può
uscire dalla chiamata prevista ed eseguire JavaScript arbitrario, ottenendo esecuzione di codice remoto
sul server.
Il runner fork imposta require = null come sandbox, ma è banale da aggirare:
è ancora raggiungibile e avvia un vero processo del sistema operativo.
process.binding("spawn_sync")Stavo controllando il sottosistema dei runner di DbGate per individuare il divario tra i percorsi di
esecuzione del codice protetti e non protetti. Il runner start() in runners.js:292 è
correttamente protetto — chiama testStandardPermission('run-shell-script') e verifica
platformInfo.allowShellScripting.
loadReader() fa qualcosa di concettualmente simile (costruisce ed esegue uno script loader JS) ma non
ha nessuno di questi controlli. Ho tracciato il flusso dei dati:
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 è controllato dall'attaccante e finisce all'interno di una stringa template eseguita. Il
prefisso dbgateApi. è l'unica cosa che lo precede ed è aggirabile: chiudendo l'espressione con
toString();, iniettando il payload e commentando il (${props}) finale con // si ottiene JS valido.
File: packages/api/src/controllers/runners.js (loadReader → loaderScriptTemplate)
File: 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)});`
Script generato (iniettato):
const reader = await dbgateApi.toString();
process.binding("spawn_sync").spawn({ file:"/bin/sh", args:["/bin/sh","-c","id"], ... });
dbgateApi.toString//({});
compileShellApiFunctionName() è stata scritta per trasformare un nome breve in un percorso API
completo (dbgateApi.<name>), fidandosi implicitamente che functionName sia un identificatore.
Il valore, però, arriva direttamente dal corpo della richiesta HTTP. Poiché viene concatenato in
sorgente che viene poi passata a eval/fork, questa assunzione di fiducia diventa una RCE. La sandbox
require = null non aiuta — il process.binding("spawn_sync") interno di Node la aggira.
Correzione (7.1.9): validare functionName tramite una allow-list rigorosa di identificatori prima
di compilarlo nello script loader.
poc/rce_loadreader_functionname_injection.py — pilota l'endpoint reale dall'inizio alla fine.
# 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'
Lo script costruisce il payload functionName, lo invia a POST /runners/load-reader e la chiamata
spawn_sync iniettata esegue il comando nel processo runner fork.
RCE autenticata sull'host dell'API DbGate. DbGate è una GUI di gestione database spesso distribuita con ampia raggiungibilità di rete verso database interni — l'esecuzione di codice su di essa è un forte punto d'appoggio verso il livello dati.
Divulgazione responsabile. PoC pubblicata dopo il rilascio della correzione, per difensori e ingegneria del rilevamento.