GHSA-4w49-gwv8-fpjg CVE-2026-60091 PYSEC-2026-4249
PraisonAI: Jobs API is unauthenticated by default and allows attacker-controlled webhook SSRF
- Severity
- 6.9 / 10
- Medium · CVSS 4.0
- Exploit chance (EPSS)
- 0.22%
- Chance of exploitation in the next 30 days. Higher than 12.62% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Weakness
- CWE-200 Exposure of Sensitive Information to an Unauthorized Actor
- CWE-306 Missing Authentication for Critical Function
- CWE-367 Time-of-check Time-of-use (TOCTOU) Race Condition
- CWE-918 Server-Side Request Forgery (SSRF)
- Fix
4.6.78- Upgrade praisonai to this version or later
Description
Summary
PraisonAI's Async Jobs API enables its API-key middleware only when PRAISONAI_JOBS_API_KEY is set, so by default every endpoint is unauthenticated. An unauthenticated POST /api/v1/runs accepts an attacker-controlled webhook_url; on job completion the server POSTs the job payload to it via httpx. The webhook_url has an SSRF validator (gethostbyname + private-IP check), but it validates at request time while httpx re-resolves at connection time — a DNS-rebinding TOCTOU that reaches internal services. Runtime-confirmed as an unauthenticated, blind SSRF (the internal canary received the POST; the internal response is not returned to the attacker). Severity Medium.
Details
Affected component
- Package:
praisonai4.6.63. Files:src/praisonai/praisonai/jobs/server.py,jobs/router.py,jobs/executor.py,jobs/models.py.
Vulnerable code / root cause
Path:
src/praisonai/praisonai/jobs/server.py
Function:
create_app
Snippet:
jobs_api_key = os.environ.get("PRAISONAI_JOBS_API_KEY")
# ...
if jobs_api_key:
app.add_middleware(JobsAPIKeyMiddleware) # auth ONLY when env var is set
Issue: with the env var unset (default), no auth middleware is added → all endpoints unauthenticated. Default bind is 127.0.0.1.
Path:
src/praisonai/praisonai/jobs/router.py
Function:
submit_job
Snippet:
@router.post("", response_model=JobSubmitResponse, status_code=202)
async def submit_job(request, response, body: JobSubmitRequest, ...):
# no auth dependency; body.webhook_url is attacker-controlled
job = Job(prompt=body.prompt, webhook_url=body.webhook_url, ...)
await executor.submit(job)
Issue: attacker-controlled webhook_url flows into the job with no authentication on the endpoint.
Path:
src/praisonai/praisonai/jobs/models.py (validator) and src/praisonai/praisonai/jobs/executor.py (sink)
Function:
validate_webhook_url → _send_webhook
Snippet:
# models.py validate_webhook_url (CHECK time)
ip = socket.gethostbyname(hostname)
if ipaddress.ip_address(ip).is_private or ...:
raise ValueError("Webhook URL resolves to a private or restricted network address")
# executor.py _send_webhook (CONNECT time, re-resolves, no pinning)
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(job.webhook_url, json=payload, ...)
Issue: the validator resolves the hostname at validation time but does not pin the IP for the httpx.post connection → DNS rebinding (independent resolution at check vs connect) bypasses it and reaches internal services.
Attack flow
- Operator runs the Jobs API without
PRAISONAI_JOBS_API_KEY(default → unauth). - Attacker
POST /api/v1/runswithwebhook_url= a rebinding domain. - The validator(s) see a public IP (pass); on completion
httpx.postre-resolves to an internal IP and connects → SSRF to internal.
Why existing protection is bypassed
Auth is opt-in (only when the env var is set). The webhook_url validator resolves at check time but does not pin the IP for the connection → DNS-rebinding TOCTOU. (Correction to an earlier static note: the guard exists but is bypassable.)
Security boundary
Unauthenticated network peer → server-side request to internal services (Scope: Changed). Default bind 127.0.0.1 limits remote reach unless the operator binds non-loopback.
Proof of Concept
Environment
Real Jobs API (python -m praisonai.jobs.server, no API key) in a local runtime (127.0.0.1:18085), resolver pointed at the controlled rebinding DNS, internal canary Docker-internal only. Runnable assets: PraisonAI-Runtime-Repro\runtime-files\ (docker-compose.jobs.yml).
Steps to reproduce
PRAI-04-01-Jobs-Submit-NoAuth:POST /api/v1/runs {"prompt":"hello"}→202(no auth).PRAI-04-02-Jobs-Webhook-SSRF-Blind:POST /api/v1/runs {"prompt":"hello","webhook_url":"http://rebind.lab:8081/secret"}→202.
Expected result
Unauthenticated job submission should be rejected; the webhook SSRF guard should prevent reaching internal services regardless of DNS timing.
Impact
Unauthenticated job submission (LLM cost abuse); SSRF to internal services (blind, DNS-rebinding); exfiltration of the job result to an attacker-controlled webhook.
Suggested remediation
- Authenticate the Jobs API by default (auto-generate a token / fail closed when binding non-loopback), like the gateway.
- For
webhook_url: resolve once, reject private/loopback/CGNAT/metadata, then pin and connect to the validated IP; disable redirects.
Affected versions
PyPI
| Package | Vulnerable | Fixed in |
|---|---|---|
| praisonai | < 4.6.78 |
4.6.78 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
praisonai PyPI → 4.6.78
- pip
pip install --upgrade "praisonai>=4.6.78"- poetry
poetry add "praisonai>=4.6.78"- uv
uv add "praisonai>=4.6.78"
Severity breakdown
CVSS 4.0 6.9
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:L/VI:L/VA:N/SC:L/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X
- Attack vector
- Network
- Attack complexity
- Low
- Attack requirements
- None
- Privileges required
- None
- User interaction
- None
- Confidentiality (vulnerable system)
- Low
- Integrity (vulnerable system)
- Low
- Availability (vulnerable system)
- None
- Confidentiality (subsequent systems)
- Low
- Integrity (subsequent systems)
- Low
- Availability (subsequent systems)
- None
- Exploit maturity
- Not defined
- Scope
- X
CVSS 3.1 7.2
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:L/I:L/A:N
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- None
- Scope
- Changed
- Confidentiality
- Low
- Integrity
- Low
- Availability
- None
Weakness
CWE-200: Exposure of Sensitive Information to an Unauthorized Actor
The product exposes sensitive information to an actor that is not explicitly authorized to have access to that information.
How to prevent it
- Architecture and Design: Compartmentalize the system to have "safe" areas where trust boundaries can be unambiguously drawn. Do not allow sensitive data to go outside of the trust boundary and always be careful when interfacing with a compartment outside of the safe area. Ensure that appropriate compartmentalization is built into the system design, and the compartmentalization allows for and reinforces privilege separation functionality. Architects and designers should rely on the principle of least privilege to decide the appropriate time to use privileges and the time to drop privileges.
CWE-306: Missing Authentication for Critical Function
The product does not perform any authentication for functionality that requires a provable user identity or consumes a significant amount of resources.
How to prevent it
- Architecture and Design: Divide the software into anonymous, normal, privileged, and administrative areas. Identify which of these areas require a proven user identity, and use a centralized authentication capability. Identify all potential communication channels, or other means of interaction with the software, to ensure that all channels are appropriately protected, including those channels that are assumed to be accessible only by authorized parties. Developers sometimes perform authentication at the primary channel, but open up a secondary channel that is assumed to be private. For example, a login mechanism may be listening on one network port, but after successful authentication, it may open up a second port where it waits for the connection, but avoids authentication because it assumes that only the authenticated party will connect to the port. In general, if the software or protocol allows a single session or user state to persist across multiple connections or channels, authentication and appropriate credential management need to be used throughout.
- Architecture and Design: For any security checks that are performed on the client side, ensure that these checks are duplicated on the server side, in order to avoid CWE-602. Attackers can bypass the client-side checks by modifying values after the checks have been performed, or by changing the client to remove the client-side checks entirely. Then, these modified values would be submitted to the server.
- Architecture and Design: Where possible, avoid implementing custom, "grow-your-own" authentication routines and consider using authentication capabilities as provided by the surrounding framework, operating system, or environment. These capabilities may avoid common weaknesses that are unique to authentication; support automatic auditing and tracking; and make it easier to provide a clear separation between authentication tasks and authorization tasks. In environments such as the World Wide Web, the line between authentication and authorization is sometimes blurred. If custom authentication routines are required instead of those provided by the server, then these routines must be applied to every single page, since these pages could be requested directly.
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid. For example, consider using libraries with authentication capabilities such as OpenSSL or the ESAPI Authenticator [REF-45].
- Implementation, System Configuration, Operation: When storing data in the cloud (e.g., S3 buckets, Azure blobs, Google Cloud Storage, etc.), use the provider's controls to require strong authentication for users who should be allowed to access the data [REF-1297] [REF-1298] [REF-1302].
CWE-367: Time-of-check Time-of-use (TOCTOU) Race Condition
The product checks the state of a resource before using that resource, but the resource's state can change between the check and the use in a way that invalidates the results of the check.
How to prevent it
- Implementation: The most basic advice for TOCTOU vulnerabilities is to not perform a check before the use. This does not resolve the underlying issue of the execution of a function on a resource whose state and identity cannot be assured, but it does help to limit the false sense of security given by the check.
- Implementation: When the file being altered is owned by the current user and group, set the effective gid and uid to that of the current user and group when executing this statement.
- Architecture and Design: Limit the interleaving of operations on files from multiple processes.
- Implementation, Architecture and Design: If you cannot perform operations atomically and you must share access to the resource between multiple processes or threads, then try to limit the amount of time (CPU cycles) between the check and use of the resource. This will not fix the problem, but it could make it more difficult for an attack to succeed.
- Implementation: Recheck the resource after the use call to verify that the action was taken appropriately.
- Architecture and Design: Ensure that some environmental locking mechanism can be used to protect resources effectively.
- Implementation: Ensure that locking occurs before the check, as opposed to afterwards, such that the resource, as checked, is the same as it is when in use.
CWE-918: Server-Side Request Forgery (SSRF)
The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
References
Advisories
Packages
Other links
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-26mh-57q7-jfvr PraisonAI vulnerable to Remote Code Execution via Broken AST Sandbox | High | Also affects praisonai |
| GHSA-6g6r-q6gw-w8fg PraisonAI has a Browser Server WebSocket origin validation bypass via unanchored regex (patch bypass of CVE-2026-40289 / GHSA-8x8f-54wf-vv92) | Critical | Also affects praisonai |
| GHSA-pvph-5j39-v8qc PraisonAI: Origin-validation bypass (startswith prefix match) enables unauthenticated cross-site request forgery against the PraisonAI MCP HTTP server | High | Also affects praisonai |
| GHSA-gfq8-hmph-9gjv PraisonAI: Authentication fail-open in Recipe server allows unauthenticated access when API key or JWT auth is configured without a secret | High | Also affects praisonai |
| GHSA-2jgc-f764-c5r2 PraisonAI: [Auth Bypass] PraisonAI async Jobs API (`/api/v1/runs`) has no authentication — unauthenticated job execution, result theft, cancel and delete | High | Also affects praisonai |
| GHSA-pvxx-r596-f5qj PraisonAI: `--api-key` flag on `praisonai serve` is not properly enforced | High | Also affects praisonai |
| GHSA-hmfx-4v44-9qw9 PraisonAI vulnerable to Server-Side Request Forgery via DNS rebinding bypass in webhook_url validation | Medium | Also affects praisonai |
| GHSA-rg5q-pp8p-f7jm PraisonAI: Webhook SSRF via DNS fail-open in `JobSubmitRequest.validate_webhook_url()` — bypass of CVE-2026-40114 | High | Also affects praisonai |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor