~/f4n6 $ grep -r "PraisonAI: MCP HTTP-stream transport is unauthenticated by default, exposing tool enumeration and an unvalidated tool-call surface" ./investigations/ --include="*.md"

PraisonAI: MCP HTTP-stream transport is unauthenticated by default, exposing tool enumeration and an unvalidated tool-call surface

Jeff Davies 09 Oct 2026 8 min read

1. Executive summary

PraisonAI 4.6.63 ships an MCP HTTP-stream server that performs no authentication unless an API key is explicitly configured, and the CLI defaults --api-key to None. An unauthenticated client that sends no Authorization and no Origin header can complete initialize, enumerate roughly 50 tools via tools/list, and reach the tool dispatcher, which forwards call arguments to handlers without validating them against the advertised inputSchema. The default bind is 127.0.0.1, so exposure is limited to any local process or container neighbour on loopback unless the operator has set --host 0.0.0.0. No CVE record, CVSS score, or CISA KEV entry was resolved for this item, so no exploitation-in-the-wild status can be stated. The immediate risk to EMEA financial services clients is unauthenticated access to whatever MCP tools function in their deployment, including LLM key and cost abuse and file-read sinks documented in adjacent advisories.

2. Regulatory framing

No specific DORA/NIS2 article is directly engaged by this item. The advisory describes a default-configuration authentication gap in an open-source Python package, not an incident at a client, and the source provides no evidence of exploitation against any regulated entity. Mapping DORA Art. 17 or NIS2 Art. 23 to a package advisory with no confirmed incident would be a compliance-checkbox exercise. The article below is engaged only if a client has deployed the affected server and can confirm unauthorised access.

Article Trigger (the fact in this item) Practical impact
DORA Art. 17: ICT-related incident management process Only if a client's own PraisonAI MCP server was reachable and shows unauthenticated initialize/tools/list/tools/call in its logs The client's incident management process must handle the event; this advisory alone does not trigger it

3. Technical analysis & attack chain

The flaw is a default-configuration authentication gap in the MCP HTTP-stream transport, compounded by a dispatcher that does not enforce tool input schemas. Both behaviours were runtime-confirmed by the reporter against a real server on 127.0.0.1:18090 with api_key=None.

Confirmed attack chain:

  1. Operator starts the server with praisonai mcp serve --transport http-stream and does not pass --api-key. The argparse definition in src/praisonai/praisonai/mcp_server/cli.py sets default=None.
  2. Attacker sends POST /mcp with method initialize, no Authorization header, and no Origin header. The server returns HTTP 200, exposes MCP capabilities, and issues an mcp-session-id.
  3. Attacker sends POST /mcp with method tools/list using only the issued Mcp-Session-Id. The server returns HTTP 200 and enumerates roughly 50 tools with their names, schemas, and annotations.
  4. Attacker sends tools/call with an argument not declared in the tool's inputSchema. The dispatcher passes it to the Python handler, which raises got an unexpected keyword argument '__undeclared_evil_param__'. The error proves the argument reached the handler, confirming no schema validation at the dispatcher layer.

Root cause, in three parts:

  • src/praisonai/praisonai/mcp_server/transports/http_stream.py, functions mcp_post and _validate_origin. The auth check is gated on if self.api_key:, so with api_key=None no check runs. _validate_origin returns True when the Origin header is absent, so non-browser clients such as curl and Burp are not blocked.
  • src/praisonai/praisonai/mcp_server/cli.py, function cmd_serve. parser.add_argument("--api-key", default=None) makes authentication opt-in.
  • src/praisonai/praisonai/mcp_server/server.py, function _handle_tools_call. result = await tool.handler(**arguments) forwards attacker-controlled arguments straight to the handler. The only thing rejecting undeclared keys is the handler's own Python signature.

Security boundary and scope limits. The default bind is 127.0.0.1, so the surface is reachable from any local process or any other tenant on a multi-user host, and remotely only if the operator set --host 0.0.0.0. The reporter explicitly refutes unauthenticated RCE and arbitrary file read in 4.6.63: praisonai.workflow.run and workflow.run_file are runtime-refuted because the adapter calls AgentsGenerator(...) without the required config_list argument and errors before any execution or file open. Several other tool adapters also error at runtime, and the MCP knowledge.add file read is broken. Do not treat this as an RCE advisory.

Adjacent advisories describe the same dispatcher pattern with confirmed file-read impact, and clients running PraisonAI should treat them as one exposure. GHSA-9cr9-25q5-8prj reports that the fix for CVE-2026-44336 was incomplete: praisonai.workflow.show in mcp_server/adapters/cli_tools.py (lines 63-73) returns the contents of any file the host user can read, including /etc/passwd, ~/.ssh/id_rsa, ~/.aws/credentials, and project .env files, via a single unauthenticated tools/call. praisonai.workflow.validate (lines 42-61) and praisonai.deploy.validate (lines 415-432) read arbitrary paths and leak file existence and content fragments through YAML parser errors. The same advisory names further file-read sinks reachable through the dispatcher: praisonai.audio.transcribe(file_path) in mcp_server/adapters/capabilities.py (lines 19-28), which ships any host file to OpenAI Whisper; praisonai.files.create(file_path) in mcp_server/adapters/extended_capabilities.py (lines 47-62), which uploads any host file to OpenAI Files, with praisonai.files.content(file_id) (lines 103-113) returning the bytes; and praisonai.ocr_extract(image_path) (lines 243-258). Those handlers are also reachable over the stdio transport used by Claude Desktop, Cursor, Continue.dev, and Claude Code. The dispatcher code at server.py:281-298 was unchanged in HEAD as of commit 42221210.

A third advisory, GHSA-7ww9-85pg-cv4x, covers a separate code path: praisonai serve agents --api-key accepts and forwards the key but _create_agents_app() never uses config["api_key"] to create middleware or a FastAPI auth dependency, so POST /agents and POST /agents/{agent_name} are reachable without credentials. Confirmed vulnerable versions are v4.6.48 (commit d5f1114aaf1a2e9f121a6e66b929149ca2201f1d) and v4.6.34 (commit e5928449f73f66cc8af1de61621aa974ab255133), with a likely affected range of >= 4.6.34, <= 4.6.48. The protected /api/v1/agents/{agent_id}/invoke route, guarded by CALL_SERVER_TOKEN, coexists in the same server as the unauthenticated compatibility routes.

Confidence caveats. The runtime-confirmed behaviours (unauthenticated initialize, unauthenticated tools/list, dispatcher schema bypass) rest on the reporter's proof of concept in GHSA-hc5v-gxvj-58wh, which is single-sourced. The file-read impact in GHSA-9cr9-25q5-8prj is a separate single-sourced report; the source text supplied here is truncated mid-sentence in its proof-of-concept section, so the file-read claim should be verified against the full advisory before it is treated as confirmed. The serve agents finding in GHSA-7ww9-85pg-cv4x is single-sourced and its proof of concept is local-only, using FastAPI TestClient with a fake agent and no LLM provider call. No exploitation in the wild is reported in any of the three advisories.

4. Mitigation & containment

P1 within 24h

  • Identify every host running praisonai mcp serve --transport http-stream and every host running praisonai serve agents. Search process listings and container definitions for those command lines.
  • Where the server is not required, stop it. Where it is required, restart it with an explicit --api-key value and confirm the transport now returns 401 to a request with no Authorization header. Note that setting --api-key does not protect the serve agents compatibility routes described in GHSA-7ww9-85pg-cv4x; those need the network control below.
  • Confirm the bind address. If the process is listening on 0.0.0.0, restrict it to 127.0.0.1 unless remote access is a documented requirement, and block inbound access to the MCP port at the host firewall and any container network policy.
  • For any server that has been reachable, review logs for POST /mcp requests carrying initialize or tools/list without an Authorization header, and for POST /agents or POST /agents/{agent_name} without credentials. Treat any such request as unauthorised access to the tool surface.

P2 within 72h

  • Rotate every credential reachable from the host user account that ran the server, including LLM provider API keys, ~/.aws/credentials, ~/.ssh/id_rsa, and any .env file in the project directory. The file-read sinks documented in GHSA-9cr9-25q5-8prj return exactly these paths.
  • Audit OpenAI usage and billing for the account tied to the deployment. praisonai.audio.transcribe and praisonai.files.create ship host files to OpenAI endpoints, and any functioning tool that calls an LLM can be driven for cost abuse.
  • Pin praisonai to a version that carries a fix once one is published. No fixed version is named in the source material supplied here; track GHSA-hc5v-gxvj-58wh, GHSA-9cr9-25q5-8prj, and GHSA-7ww9-85pg-cv4x for the patched release and do not assume 4.6.63 is fixed.
  • Where the package cannot be patched immediately, place the MCP server behind an authenticating reverse proxy that rejects requests lacking a valid credential, and reject requests with no Origin header at that proxy.

P3 within 7 days

  • Inventory other MCP servers in the estate and check each for the same two patterns: authentication that is opt-in rather than default, and a dispatcher that forwards tool-call arguments without validating them against the declared schema. This is a class of defect, not a single package.
  • Add a build-time or CI check that fails when a PraisonAI service is started without --api-key, and a runtime check that alerts when the MCP port accepts a request with no Authorization header.

5. Indicators of compromise

No atomic indicators of compromise are available in the source material. The sources describe observable behaviours rather than hashes, domains, or IP addresses.

Behavioural indicators:

behaviour where to observe confidence
POST /mcp initialize returning HTTP 200 and an mcp-session-id with no Authorization header MCP HTTP-stream server access log high, runtime-confirmed by reporter
POST /mcp tools/list returning roughly 50 tools using only Mcp-Session-Id MCP HTTP-stream server access log high, runtime-confirmed by reporter
tools/call producing got an unexpected keyword argument for an undeclared parameter application log / stderr of the MCP server high, runtime-confirmed by reporter
POST /agents or POST /agents/{agent_name} succeeding without Authorization, X-API-Key, or query token FastAPI access log for praisonai serve agents medium, single-sourced (GHSA-7ww9-85pg-cv4x)
outbound calls to OpenAI Whisper or OpenAI Files from the MCP host for files not expected to leave it egress logs, OpenAI account audit medium, single-sourced (GHSA-9cr9-25q5-8prj)

6. Detection

The sources give usable artefacts for a Sigma rule: the MCP method names, the transport path, and the header names. A YARA rule is not appropriate here because the source provides no distinctive strings from a malicious file; the artefacts are protocol-level and belong in log detection.

title: PraisonAI MCP HTTP-stream unauthenticated tool enumeration
id: 7f3c1a92-4b6e-4d21-9c8a-2e5f0b7d1a44
status: experimental
description: Detects MCP initialize or tools/list requests to the PraisonAI HTTP-stream transport that carry no Authorization header, consistent with GHSA-hc5v-gxvj-58wh.
author: Adverse Trace
date: 2026-10-09
references:

  - https://github.com/advisories/GHSA-hc5v-gxvj-58wh
logsource:
  category: webserver
detection:
  selection_path:
    cs-uri-path|endswith: '/mcp'
  selection_method:

    - '"method":"initialize"'
    - '"method": "initialize"'
    - '"method":"tools/list"'
    - '"method": "tools/list"'
  filter_authorized:
    cs-header|contains: 'Authorization'
  condition: selection_path and selection_method and not filter_authorized
falsepositives:

  - Health checks or internal tooling that legitimately calls the MCP endpoint without a bearer token on a loopback-only deployment
level: medium
title: PraisonAI serve agents unauthenticated invocation of compatibility routes
id: 2a8d5e10-9c47-4f83-b1d6-6e0a3c9f7b21
status: experimental
description: Detects POST requests to the PraisonAI serve agents compatibility routes that carry no Authorization or X-API-Key header, consistent with GHSA-7ww9-85pg-cv4x.
author: Adverse Trace
date: 2026-10-09
references:

  - https://github.com/advisories/GHSA-7ww9-85pg-cv4x
logsource:
  category: webserver
detection:
  selection_method:
    cs-method: 'POST'
  selection_path:
    cs-uri-path|startswith: '/agents'
  filter_authorized:

    - cs-header|contains: 'Authorization'
    - cs-header|contains: 'X-API-Key'
  condition: selection_method and selection_path and not filter_authorized
falsepositives:

  - Internal callers on a trusted network segment where the operator has accepted the risk
level: medium

7. Sources

  • GitHub Security Advisories (pip), "PraisonAI: MCP HTTP-stream transport is unauthenticated by default, exposing tool enumeration and an unvalidated tool-call surface", https://github.com/advisories/GHSA-hc5v-gxvj-58wh, published 2026-10-08.
  • GitHub Security Advisories (high+), "PraisonAI vulnerable to unauthenticated arbitrary file read via MCP workflow.show, workflow.validate, deploy.validate", https://github.com/advisories/GHSA-9cr9-25q5-8prj.
  • GitHub Security Advisories (high+), "PraisonAI serve agents --api-key is ignored, allowing unauthenticated remote agent execution", https://github.com/advisories/GHSA-7ww9-85pg-cv4x.
  • GitHub Advisory Database, "CVE-2026-61427", https://github.com/advisories/GHSA-hc5v-gxvj-58wh.

8. Adverse Trace position

No CVSS score, severity rating, or CISA KEV entry was resolved for this item, so we assess it on the source evidence alone: a default-configuration authentication gap with runtime-confirmed unauthenticated tool enumeration and a dispatcher that does not enforce input schemas, with confirmed file-read impact documented in an adjacent advisory against the same dispatcher. The default loopback bind limits remote exposure, but multi-user hosts and container neighbours on loopback are in scope, and any deployment that set --host 0.0.0.0 is remotely reachable. We treat the file-read claims in GHSA-9cr9-25q5-8prj and the serve agents claims in GHSA-7ww9-85pg-cv4x as single-sourced and unverified by us; clients should verify against the full advisories before acting on them. We will track the three advisories for a patched release and update this note when a fixed version is named. Clients running PraisonAI in any MCP or agent-serving capacity should confirm their bind address and authentication configuration today and report back so we can scope the exposure across the estate.


Read the original source →

Published via PulseTrace — Adverse Trace threat intelligence.

Post this to LinkedIn
Formatting is converted automatically — headings, bullets, a link back & hashtags. Paste straight in.
J
Jeff Davies