GHSA-h9f9-h6gm-wc85 flyto-core has Unauthenticated Command Execution via HTTP MCP `execute_module`
Unauthenticated Command Execution via HTTP MCP `execute_module`SummaryThe 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.Detailsflyto-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-78`** — `mcp_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-838`** — `tools/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-139`** — `command` is read directly from `params` with no sanitization.**`src/core/modules/atomic/sandbox/execute_shell.py:163-169`** — `command` 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:****Contrast with the protected REST route:**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):****Exploit (curl) — no Authorization header:****Exploit (Python PoC script):****Observed response (dynamic reproduction, Phase 2):**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:**ImpactThis 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``poc.py`
CVSS v3.1 score: 8.4