GHSA-P75F-6FP4-P57W

Vulnerability from github – Published: 2026-06-18 13:58 – Updated: 2026-06-18 13:58
VLAI
Summary
PraisonAI: Missing Authentication for Critical Function and Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') in praisonai
Details

Unauthenticated PraisonAI UI MCP connect endpoint executes attacker-chosen local commands

Summary

PraisonAI v4.6.48 exposes the PraisonAIUI MCP client management API through the default UI host apps without authentication. A remote unauthenticated client can send POST /api/mcp/connect with a command and args field. The endpoint passes those values into the MCP stdio client, which starts the attacker-selected local process as the PraisonAI UI service user.

The issue is reachable through PraisonAI's hosted UI integration (praisonai ui, praisonai ui agents, praisonai claw, and any app using praisonai.integration.host_app.create_host_app() / build_host_app()). praisonai ui and related Typer UI commands bind to 0.0.0.0 by default.

Affected Versions

Confirmed affected:

  • praisonai v4.6.48
  • Commit tested: d5f1114aaf1a2e9f121a6e66b929149ca2201f1d
  • Tag tested: v4.6.48
  • Pinned UI dependency: aiui==0.3.121 from src/praisonai/uv.lock

Likely affected:

  • Any PraisonAI release that exposes aiui / praisonaiui create_app() through the PraisonAI UI host apps without authentication and includes the mcp dependency. I only confirmed the latest release during this audit.

Severity

Reasoning:

  • AV: the vulnerable endpoint is an HTTP API route.
  • AC: a single POST request is sufficient.
  • PR: default UI host apps do not require credentials unless opt-in auth is configured.
  • UI: no victim interaction is needed after the server is running.
  • S: code executes in the PraisonAI UI server process context.
  • C/I/A: arbitrary local command execution permits secret exfiltration, file tampering, and service disruption.

Root Cause

PraisonAI depends on MCP by default and exposes PraisonAIUI via optional UI extras:

  • src/praisonai/pyproject.toml:11 includes base dependencies.
  • src/praisonai/pyproject.toml:19 includes mcp>=1.20.0.
  • src/praisonai/pyproject.toml:25 defines the ui extra with aiui>=0.3.121,<0.4.
  • src/praisonai/pyproject.toml:197 defines the claw extra with aiui[all]>=0.3.121,<0.4.

PraisonAI's UI commands bind externally by default and launch aiui run:

  • src/praisonai/praisonai/cli/commands/ui.py:114 sets host="0.0.0.0" for praisonai ui.
  • src/praisonai/praisonai/cli/commands/ui.py:163 passes that host to aiui run.
  • src/praisonai/praisonai/cli/commands/ui.py:186, :204, and :222 also default subcommands to 0.0.0.0.
  • src/praisonai/praisonai/cli/commands/claw.py:41 defines the full dashboard command.
  • src/praisonai/praisonai/cli/commands/claw.py:93 launches aiui run with the selected host.

PraisonAI's default apps create the PraisonAIUI Starlette app without forcing authentication:

  • src/praisonai/praisonai/ui_chat/default_app.py:18 calls configure_host(...).
  • src/praisonai/praisonai/ui_chat/default_app.py:142 exports app = create_host_app().
  • src/praisonai/praisonai/claw/default_app.py:63 calls configure_host(...).
  • src/praisonai/praisonai/claw/default_app.py:128 exports app = create_host_app().
  • src/praisonai/praisonai/integration/host_app.py:174 imports praisonaiui.server.create_app.
  • src/praisonai/praisonai/integration/host_app.py:180 returns create_app().

In aiui==0.3.121, the exposed server registers the MCP routes and auth is opt-in:

  • praisonaiui/server.py:1483 defines api_mcp_connect.
  • praisonaiui/server.py:1488 reads attacker-controlled JSON.
  • praisonaiui/server.py:1491 accepts either command or url.
  • praisonaiui/server.py:1496 calls connect_mcp_server(body).
  • praisonaiui/server.py:2516 defines create_app(..., require_auth=False, ...).
  • praisonaiui/server.py:2550 adds AuthEnforcementMiddleware, but it only enforces auth when AUTH_ENFORCE=true.
  • praisonaiui/server.py:2769 registers /api/mcp/servers.
  • praisonaiui/server.py:2770 registers /api/mcp/connect.

The MCP feature converts the request body into a local process launch:

  • praisonaiui/features/mcp.py:325 defines connect_server(self, server_config).
  • praisonaiui/features/mcp.py:330 chooses stdio transport when command is present.
  • praisonaiui/features/mcp.py:332 constructs StdioMCPClient(command=server_config["command"], args=server_config.get("args", [])).
  • praisonaiui/features/mcp.py:360 calls client.connect(), which invokes the MCP stdio transport and starts the process.

Minimal PoC

PoC file: poc/praisonai-aiui-mcp-connect-rce.py

The PoC runs the PraisonAI host app in-process, sends the unauthenticated HTTP request, and asks the server to execute /usr/bin/touch /tmp/praisonai_host_app_mcp_touch_marker.txt. It does not contact an LLM provider and uses no credentials.

Observed output from the tested checkout with aiui==0.3.121 and mcp==1.25.0 available:

[19:19:55] server.py:229 WARNING No auth_token provided for Gateway server. Generated temporary token: gw_****650a. For production, set GATEWAY_AUTH_TOKEN.
[19:19:55] mcp.py:135 ERROR Failed to connect to MCP stdio server: 'tuple' object has no attribute 'initialize'
HTTP_STATUS= 200
RESPONSE= {"server":{"name":"poc-stdio-process-0","transport":"stdio","status":"error","tools":[],"last_error":"Connection failed"}}
SUCCESS_AT_ATTEMPT= 0
MARKER_EXISTS= True
MARKER_PATH= /tmp/praisonai_host_app_mcp_touch_marker.txt

The MCP handshake fails because aiui==0.3.121 is not compatible with the locked mcp==1.25.0 return shape, but the attacker-selected process is already started. Process startup can race the immediate teardown caused by this version mismatch, so the checked-in PoC retries the same unauthenticated request until /usr/bin/touch wins scheduling and creates the marker. The marker file proves local command execution despite the reported MCP connection error.

Exploit Scenario

An operator runs:

pip install "praisonai[ui]"
praisonai ui

Because praisonai ui binds to 0.0.0.0 by default and the generated app does not require authentication by default, any host that can reach the UI port can send:

POST /api/mcp/connect
Content-Type: application/json

{
  "name": "evil",
  "command": "/usr/bin/touch",
  "args": ["/tmp/pwned-by-ui-mcp"]
}

In a real attack, the command can be replaced with a shell, a credential exfiltration command, a file modification command, or a payload that starts a long-lived process as the PraisonAI UI server user.

Novelty / Non-Duplicate Analysis

Searched sources:

  • OSV query for PyPI praisonai: 51 advisories returned.
  • OSV query for PyPI aiui: 0 advisories returned.
  • OSV query for PyPI praisonaiui: 0 advisories returned.
  • GitHub Advisory Database search for exact /api/mcp/connect, api_mcp_connect, StdioMCPClient, connect_mcp_server, and praisonaiui.features.mcp.
  • NVD API searches for PraisonAI StdioMCPClient, PraisonAI api_mcp_connect, PraisonAI /api/mcp/connect, PraisonAIUI /api/mcp/connect, and aiui StdioMCPClient: 0 results.
  • GitHub issue/PR searches in MervinPraison/PraisonAI for exact endpoint/function/class terms. Only one unrelated PR was returned for /api/mcp/connect; no issue/PR matched api_mcp_connect, StdioMCPClient, connect_mcp_server, or praisonaiui.features.mcp.
  • Broad web searches for exact endpoint, file, class, and function terms returned no matching public vulnerability report.

Why this is distinct from known PraisonAI advisories:

  • Not the excluded praisonai serve agents --api-key /agents auth bypass. This report targets POST /api/mcp/connect in the PraisonAIUI host app.
  • Not GHSA-9gm9-c8mq-vq7m / CVE-2026-34935 or GHSA-9qhq-v63v-fv3j / CVE-2026-41497. Those involve MCPHandler.parse_mcp_command() command parsing. This finding uses praisonaiui.server.api_mcp_connect -> praisonaiui.features.mcp.connect_mcp_server -> StdioMCPClient.
  • Not GHSA-pj2r-f9mw-vrcq / CVE-2026-40159. That advisory concerns sensitive environment variables inherited by untrusted MCP subprocesses. This finding is unauthenticated network-triggered local process execution.
  • Not GHSA-6rmh-7xcm-cpxj or GHSA-8444-4fhq-fxpq. Those concern unauthenticated legacy/generated agent servers. This is a distinct UI route and a distinct sink that starts arbitrary local processes.
  • Not GHSA-9cr9-25q5-8prj, GHSA-9mqq-jqxf-grvw, or other MCP server file-read/path-traversal advisories. This path is the UI MCP client connector, not PraisonAI's MCP server tool dispatcher.

Recommended Fix

  1. Remove arbitrary command/args from the remote HTTP API. MCP stdio servers should be configured only from trusted local configuration, not caller-supplied JSON.
  2. Require authentication and authorization on /api/mcp/connect, /api/mcp/disconnect/*, and /api/mcp/servers regardless of AUTH_ENFORCE.
  3. Change UI command defaults from 0.0.0.0 to 127.0.0.1, or require an explicit --unsafe-expose style flag when binding externally without auth.
  4. If remote MCP registration is a required feature, allow only URL-based transports with SSRF protections, or maintain an administrator-configured allowlist of commands.
  5. Add regression tests that unauthenticated requests to /api/mcp/connect cannot start a subprocess, including when AUTH_ENFORCE is unset.
Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 4.6.48"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "praisonai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.6.59"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [],
  "database_specific": {
    "cwe_ids": [
      "CWE-306",
      "CWE-78"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-18T13:58:14Z",
    "nvd_published_at": null,
    "severity": "CRITICAL"
  },
  "details": "# Unauthenticated PraisonAI UI MCP connect endpoint executes attacker-chosen local commands\n\n## Summary\n\nPraisonAI v4.6.48 exposes the PraisonAIUI MCP client management API through the default UI host apps without authentication. A remote unauthenticated client can send `POST /api/mcp/connect` with a `command` and `args` field. The endpoint passes those values into the MCP stdio client, which starts the attacker-selected local process as the PraisonAI UI service user.\n\nThe issue is reachable through PraisonAI\u0027s hosted UI integration (`praisonai ui`, `praisonai ui agents`, `praisonai claw`, and any app using `praisonai.integration.host_app.create_host_app()` / `build_host_app()`). `praisonai ui` and related Typer UI commands bind to `0.0.0.0` by default.\n\n## Affected Versions\n\nConfirmed affected:\n\n- `praisonai` v4.6.48\n- Commit tested: `d5f1114aaf1a2e9f121a6e66b929149ca2201f1d`\n- Tag tested: `v4.6.48`\n- Pinned UI dependency: `aiui==0.3.121` from `src/praisonai/uv.lock`\n\nLikely affected:\n\n- Any PraisonAI release that exposes `aiui` / `praisonaiui` `create_app()` through the PraisonAI UI host apps without authentication and includes the `mcp` dependency. I only confirmed the latest release during this audit.\n\n## Severity\n\nReasoning:\n\n- `AV`: the vulnerable endpoint is an HTTP API route.\n- `AC`: a single POST request is sufficient.\n- `PR`: default UI host apps do not require credentials unless opt-in auth is configured.\n- `UI`: no victim interaction is needed after the server is running.\n- `S`: code executes in the PraisonAI UI server process context.\n- `C/I/A`: arbitrary local command execution permits secret exfiltration, file tampering, and service disruption.\n\n## Root Cause\n\nPraisonAI depends on MCP by default and exposes PraisonAIUI via optional UI extras:\n\n- `src/praisonai/pyproject.toml:11` includes base dependencies.\n- `src/praisonai/pyproject.toml:19` includes `mcp\u003e=1.20.0`.\n- `src/praisonai/pyproject.toml:25` defines the `ui` extra with `aiui\u003e=0.3.121,\u003c0.4`.\n- `src/praisonai/pyproject.toml:197` defines the `claw` extra with `aiui[all]\u003e=0.3.121,\u003c0.4`.\n\nPraisonAI\u0027s UI commands bind externally by default and launch `aiui run`:\n\n- `src/praisonai/praisonai/cli/commands/ui.py:114` sets `host=\"0.0.0.0\"` for `praisonai ui`.\n- `src/praisonai/praisonai/cli/commands/ui.py:163` passes that host to `aiui run`.\n- `src/praisonai/praisonai/cli/commands/ui.py:186`, `:204`, and `:222` also default subcommands to `0.0.0.0`.\n- `src/praisonai/praisonai/cli/commands/claw.py:41` defines the full dashboard command.\n- `src/praisonai/praisonai/cli/commands/claw.py:93` launches `aiui run` with the selected host.\n\nPraisonAI\u0027s default apps create the PraisonAIUI Starlette app without forcing authentication:\n\n- `src/praisonai/praisonai/ui_chat/default_app.py:18` calls `configure_host(...)`.\n- `src/praisonai/praisonai/ui_chat/default_app.py:142` exports `app = create_host_app()`.\n- `src/praisonai/praisonai/claw/default_app.py:63` calls `configure_host(...)`.\n- `src/praisonai/praisonai/claw/default_app.py:128` exports `app = create_host_app()`.\n- `src/praisonai/praisonai/integration/host_app.py:174` imports `praisonaiui.server.create_app`.\n- `src/praisonai/praisonai/integration/host_app.py:180` returns `create_app()`.\n\nIn `aiui==0.3.121`, the exposed server registers the MCP routes and auth is opt-in:\n\n- `praisonaiui/server.py:1483` defines `api_mcp_connect`.\n- `praisonaiui/server.py:1488` reads attacker-controlled JSON.\n- `praisonaiui/server.py:1491` accepts either `command` or `url`.\n- `praisonaiui/server.py:1496` calls `connect_mcp_server(body)`.\n- `praisonaiui/server.py:2516` defines `create_app(..., require_auth=False, ...)`.\n- `praisonaiui/server.py:2550` adds `AuthEnforcementMiddleware`, but it only enforces auth when `AUTH_ENFORCE=true`.\n- `praisonaiui/server.py:2769` registers `/api/mcp/servers`.\n- `praisonaiui/server.py:2770` registers `/api/mcp/connect`.\n\nThe MCP feature converts the request body into a local process launch:\n\n- `praisonaiui/features/mcp.py:325` defines `connect_server(self, server_config)`.\n- `praisonaiui/features/mcp.py:330` chooses stdio transport when `command` is present.\n- `praisonaiui/features/mcp.py:332` constructs `StdioMCPClient(command=server_config[\"command\"], args=server_config.get(\"args\", []))`.\n- `praisonaiui/features/mcp.py:360` calls `client.connect()`, which invokes the MCP stdio transport and starts the process.\n\n## Minimal PoC\n\nPoC file: `poc/praisonai-aiui-mcp-connect-rce.py`\n\nThe PoC runs the PraisonAI host app in-process, sends the unauthenticated HTTP request, and asks the server to execute `/usr/bin/touch /tmp/praisonai_host_app_mcp_touch_marker.txt`. It does not contact an LLM provider and uses no credentials.\n\nObserved output from the tested checkout with `aiui==0.3.121` and `mcp==1.25.0` available:\n\n```text\n[19:19:55] server.py:229 WARNING No auth_token provided for Gateway server. Generated temporary token: gw_****650a. For production, set GATEWAY_AUTH_TOKEN.\n[19:19:55] mcp.py:135 ERROR Failed to connect to MCP stdio server: \u0027tuple\u0027 object has no attribute \u0027initialize\u0027\nHTTP_STATUS= 200\nRESPONSE= {\"server\":{\"name\":\"poc-stdio-process-0\",\"transport\":\"stdio\",\"status\":\"error\",\"tools\":[],\"last_error\":\"Connection failed\"}}\nSUCCESS_AT_ATTEMPT= 0\nMARKER_EXISTS= True\nMARKER_PATH= /tmp/praisonai_host_app_mcp_touch_marker.txt\n```\n\nThe MCP handshake fails because `aiui==0.3.121` is not compatible with the locked `mcp==1.25.0` return shape, but the attacker-selected process is already started. Process startup can race the immediate teardown caused by this version mismatch, so the checked-in PoC retries the same unauthenticated request until `/usr/bin/touch` wins scheduling and creates the marker. The marker file proves local command execution despite the reported MCP connection error.\n\n## Exploit Scenario\n\nAn operator runs:\n\n```bash\npip install \"praisonai[ui]\"\npraisonai ui\n```\n\nBecause `praisonai ui` binds to `0.0.0.0` by default and the generated app does not require authentication by default, any host that can reach the UI port can send:\n\n```http\nPOST /api/mcp/connect\nContent-Type: application/json\n\n{\n  \"name\": \"evil\",\n  \"command\": \"/usr/bin/touch\",\n  \"args\": [\"/tmp/pwned-by-ui-mcp\"]\n}\n```\n\nIn a real attack, the command can be replaced with a shell, a credential exfiltration command, a file modification command, or a payload that starts a long-lived process as the PraisonAI UI server user.\n\n## Novelty / Non-Duplicate Analysis\n\nSearched sources:\n\n- OSV query for PyPI `praisonai`: 51 advisories returned.\n- OSV query for PyPI `aiui`: 0 advisories returned.\n- OSV query for PyPI `praisonaiui`: 0 advisories returned.\n- GitHub Advisory Database search for exact `/api/mcp/connect`, `api_mcp_connect`, `StdioMCPClient`, `connect_mcp_server`, and `praisonaiui.features.mcp`.\n- NVD API searches for `PraisonAI StdioMCPClient`, `PraisonAI api_mcp_connect`, `PraisonAI /api/mcp/connect`, `PraisonAIUI /api/mcp/connect`, and `aiui StdioMCPClient`: 0 results.\n- GitHub issue/PR searches in `MervinPraison/PraisonAI` for exact endpoint/function/class terms. Only one unrelated PR was returned for `/api/mcp/connect`; no issue/PR matched `api_mcp_connect`, `StdioMCPClient`, `connect_mcp_server`, or `praisonaiui.features.mcp`.\n- Broad web searches for exact endpoint, file, class, and function terms returned no matching public vulnerability report.\n\nWhy this is distinct from known PraisonAI advisories:\n\n- Not the excluded `praisonai serve agents --api-key` `/agents` auth bypass. This report targets `POST /api/mcp/connect` in the PraisonAIUI host app.\n- Not GHSA-9gm9-c8mq-vq7m / CVE-2026-34935 or GHSA-9qhq-v63v-fv3j / CVE-2026-41497. Those involve `MCPHandler.parse_mcp_command()` command parsing. This finding uses `praisonaiui.server.api_mcp_connect -\u003e praisonaiui.features.mcp.connect_mcp_server -\u003e StdioMCPClient`.\n- Not GHSA-pj2r-f9mw-vrcq / CVE-2026-40159. That advisory concerns sensitive environment variables inherited by untrusted MCP subprocesses. This finding is unauthenticated network-triggered local process execution.\n- Not GHSA-6rmh-7xcm-cpxj or GHSA-8444-4fhq-fxpq. Those concern unauthenticated legacy/generated agent servers. This is a distinct UI route and a distinct sink that starts arbitrary local processes.\n- Not GHSA-9cr9-25q5-8prj, GHSA-9mqq-jqxf-grvw, or other MCP server file-read/path-traversal advisories. This path is the UI MCP client connector, not PraisonAI\u0027s MCP server tool dispatcher.\n\n## Recommended Fix\n\n1. Remove arbitrary `command`/`args` from the remote HTTP API. MCP stdio servers should be configured only from trusted local configuration, not caller-supplied JSON.\n2. Require authentication and authorization on `/api/mcp/connect`, `/api/mcp/disconnect/*`, and `/api/mcp/servers` regardless of `AUTH_ENFORCE`.\n3. Change UI command defaults from `0.0.0.0` to `127.0.0.1`, or require an explicit `--unsafe-expose` style flag when binding externally without auth.\n4. If remote MCP registration is a required feature, allow only URL-based transports with SSRF protections, or maintain an administrator-configured allowlist of commands.\n5. Add regression tests that unauthenticated requests to `/api/mcp/connect` cannot start a subprocess, including when `AUTH_ENFORCE` is unset.",
  "id": "GHSA-p75f-6fp4-p57w",
  "modified": "2026-06-18T13:58:14Z",
  "published": "2026-06-18T13:58:14Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-p75f-6fp4-p57w"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/MervinPraison/PraisonAI"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
      "type": "CVSS_V3"
    }
  ],
  "summary": "PraisonAI: Missing Authentication for Critical Function and Improper Neutralization of Special Elements used in an OS Command (\u0027OS Command Injection\u0027) in praisonai"
}



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…