# PraisonAI: Missing Authentication for Critical Function and Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection') in praisonai

Unauthenticated POST /api/mcp/connect on PraisonAI's default 0.0.0.0-bound UI passes attacker command/args into StdioMCPClient, spawning arbitrary local processes as the service user.

- Payout: Research
- Severity: critical
- Program: PraisonAI
- Found by: huslayer826
- Disclosed: 2026-06-18
- CVE: CVE-2026-57124
- Write-up quality: 8/10
- Original: https://github.com/advisories/GHSA-p75f-6fp4-p57w
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-p75f-6fp4-p57w
- Tags: area:ai domain:ai domain:web impact:rce kind:research platform:ghsa sev:critical tech:python technique:source-review vuln:auth-bypass vuln:command-injection vuln:misconfiguration vuln:rce

**Target:** PraisonAIUI Starlette HTTP API (/api/mcp/connect) via MCP stdio client
**Root cause:** The UI host app exposes /api/mcp/connect without authentication (auth is opt-in via AUTH_ENFORCE) and passes caller-supplied `command`/`args` straight into StdioMCPClient, which launches the process locally. UI commands also bind 0.0.0.0 by default.
**Steps:**
1. Install and run: `pip install "praisonai[ui]"` then `praisonai ui` (binds 0.0.0.0, no auth by default).
2. Send unauthenticated request: `POST /api/mcp/connect` with `Content-Type: application/json` and body `{"name":"evil","command":"/usr/bin/touch","args":["/tmp/pwned-by-ui-mcp"]}`.
3. Server reads body at server.py:1488, calls connect_mcp_server(body) at :1496.
4. features/mcp.py:330 selects stdio transport since `command` present; :332 builds `StdioMCPClient(command=..., args=...)`; :360 `client.connect()` starts the process.
5. MCP handshake may error (aiui 0.3.121 vs mcp 1.25.0 tuple mismatch) but process already spawned; retry until marker file appears.
**How found:** Source review of the aiui/praisonaiui dependency: traced the exposed /api/mcp/connect route to connect_mcp_server and StdioMCPClient, noticed auth is only enforced when AUTH_ENFORCE=true and UI defaults bind 0.0.0.0, then built an in-process PoC to confirm process spawn despite handshake failure.
**Exploitation:** Any host reaching the UI port sends one unauthenticated JSON POST; the chosen binary runs as the PraisonAI UI service user. Attacker can substitute a shell, credential-exfiltration command, file tamper, or long-lived process. PoC retries because process startup races teardown from the MCP version mismatch.
**Insight:** When a product wraps a third-party UI package, audit the wrapped package's route table and its auth defaults (here AUTH_ENFORCE opt-in) rather than the wrapper; MCP 'connect' endpoints that accept a `command` field are direct process-spawn sinks even when the protocol handshake fails afterward.

## Proof of concept (copied exactly from the original report)

**Unauthenticated POST to /api/mcp/connect with command**

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

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

**PoC script runs host app and sends request**

```
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.
```

