# DbGate: Remote Code Execution via functionName injection in loadReader endpoint

DbGate's loadReader endpoint interpolates unsanitized functionName into a JS template, letting any authenticated user inject code and run OS commands via process.binding.

- Payout: Research
- Severity: high
- Program: DbGate
- Found by: romain-deperne
- Disclosed: 2026-06-05
- CVE: CVE-2026-48017
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-hv83-ggc4-v385
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-hv83-ggc4-v385
- Tags: area:web domain:api domain:web impact:privesc impact:rce kind:research platform:ghsa sev:high tech:nodejs technique:source-review vuln:auth-bypass vuln:command-injection vuln:privilege-escalation vuln:rce

**Target:** POST /runners/load-reader API endpoint (Node.js)
**Root cause:** functionName from the request body is interpolated directly into a generated JavaScript script template by compileShellApiFunctionName without sanitization, and the endpoint lacks the run-shell-script permission and allowShellScripting checks present on the sibling start endpoint.
**Steps:**
1. Authenticate to DbGate and obtain a JWT.
2. POST to `/runners/load-reader` with body `{"functionName": "toString();var __r=process.binding(\"spawn_sync\").spawn({file:\"/bin/sh\",args:[\"/bin/sh\",\"-c\",\"id > /tmp/dbgate-rce-proof\"],...});dbgateApi.toString//", "props": {}}`.
3. The injected code breaks out of the `dbgateApi.` expression, spawns /bin/sh via process.binding("spawn_sync"), and `//` comments out the trailing template.
4. Verify `/tmp/dbgate-rce-proof` contains the output of `id`.
**How found:** Compared the loadReader endpoint against the sibling start endpoint in the same file, noticing start enforces testStandardPermission('run-shell-script') and allowShellScripting while loadReader enforces neither; then traced functionName into compileShellApiFunctionName and the script template, and realized require=null does not remove the process global.
**Exploitation:** Any authenticated basic user sends a crafted functionName; the server forks a child process running the generated script, which executes arbitrary shell commands with the Node.js process privileges, enabling file read/write and credential theft for connected databases.
**Escalation:** Pivot from basic authenticated user to full RCE and host/container compromise; read stored DB credentials to reach connected databases.
**Insight:** When a sandbox nulls out `require`, check whether `process` remains reachable — `process.binding("spawn_sync")` gives child-process spawn without require. Also diff sibling endpoints in the same controller for missing permission/platform guards.

