~/f4n6 $ grep -r "PraisonAI: [Auth Bypass] PraisonAI async Jobs API (`/api/v1/runs`) has no authentication unauthenticated job execution, result theft, cancel and delete" ./investigations/ --include="*.md"

PraisonAI: [Auth Bypass] PraisonAI async Jobs API (`/api/v1/runs`) has no authentication unauthenticated job execution, result theft, cancel and delete

Jeff Davies 25 Aug 2026 8 min read

1. Executive summary

PraisonAI’s FastAPI async Jobs API contains no authentication or job-ownership control, allowing any caller who can reach /api/v1/runs to submit agent jobs, enumerate jobs, retrieve other jobs’ results, cancel running jobs and delete completed jobs. Remote exploitation requires deployment beyond the default 127.0.0.1 bind through a public bind, published container port, reverse proxy or tunnel; exposed jobs execute attacker-controlled prompts using the operator’s configured LLM credentials.

The supplied authoritative data does not provide a CVSS score or CISA KEV state for this issue, identified by GitHub as CVE-2026-55539. It must not be conflated with legacy Flask vulnerability CVE-2026-44338, which is CVSS 7.3 HIGH, NOT in CISA KEV, has EPSS 29%, and was fixed in PraisonAI 4.6.34.

For EMEA financial services operators, an exposed instance creates direct risks of unauthorised LLM expenditure, resource exhaustion, disclosure of stored agent output and disruption of legitimate workloads. The supplied material contains no evidence of exploitation in the wild, malware deployment or threat-actor attribution.

2. Regulatory framing

No specific DORA/NIS2 article is directly engaged by this item.

3. Technical analysis & attack chain

The following chain is supported by the source-code trace and local HTTP proof of concept in the primary GitHub advisory:

  1. The Jobs API becomes reachable. The default bind is 127.0.0.1. Remote access requires an operator to bind a public interface, publish the service from a container, place it behind a reachable reverse proxy or expose it through a tunnel. The source gives the following exposed configuration:

bash python -m uvicorn praisonai.jobs.server:create_app --host 0.0.0.0 --port 8005 --factory

  1. A caller reaches routes with no authentication check. src/praisonai/praisonai/jobs/server.py creates the FastAPI application and attaches the jobs router using app.include_router(jobs_router). It configures CORS but supplies no authentication middleware or Depends(...) requirement. Allowing the Authorization header through CORS does not validate it.

src/praisonai/praisonai/jobs/router.py registers these routes under /api/v1/runs:

Method and route Capability
POST /api/v1/runs Submit a job
GET /api/v1/runs List jobs
GET /api/v1/runs/{job_id} Retrieve job status
GET /api/v1/runs/{job_id}/result Retrieve job output
POST /api/v1/runs/{job_id}/cancel Cancel a job
DELETE /api/v1/runs/{job_id} Delete a terminal job
GET /api/v1/runs/{job_id}/stream Stream job activity

The sole declared header is Idempotency-Key, which controls deduplication and is not an authorisation mechanism.

  1. An unauthenticated caller supplies an agent prompt. submit_job() builds a Job from caller-controlled JSON. The executor saves and schedules it, then the default praisonai path invokes:

python agent = Agent(instructions="You are a helpful AI assistant.", output="minimal") result = await asyncio.to_thread(agent.start, job.prompt)

The source demonstrates an unauthenticated POST /api/v1/runs returning HTTP 202. Its dynamic test substituted a canned implementation for Agent.start(), so the routing and execution call path were demonstrated without making a real LLM request. Actual provider billing impact is supported by the code path but was not dynamically validated by that test.

  1. The caller enumerates and reads cross-job data. The default in-memory store contains no owner, user or principal field. list_jobs() begins with the global self._jobs.values() collection; status and caller-supplied session_id are filters, not access controls. The proof of concept received HTTP 200 when listing jobs and when retrieving another job’s stored result.
  2. The caller interferes with workloads. The proof of concept cancelled a running job with an unauthenticated POST, receiving HTTP 200, and deleted a terminal job with an unauthenticated DELETE, receiving HTTP 204. The unauthenticated stream route is confirmed by the route definition but was not explicitly exercised in the supplied proof of concept.

Defensive interpretation

Area Supported finding
Initial access Direct HTTP access to a reachable Jobs API
Vulnerability mechanism Complete absence of route-level authentication and per-job ownership
Payload Caller-controlled prompt and job parameters, including timeout; the source states a default of 3,600 seconds
Execution context The application’s existing agent runtime and configured LLM credentials
Persistence No host persistence mechanism described; the default job store is in memory
Privilege escalation None demonstrated; abuse occurs with the application’s existing permissions
Command and control No C2 infrastructure or malware identified
Lateral movement None described
Data access Global job listing and retrieval of stored cross-job output
Impact Potential provider charges, CPU/memory and queue consumption, output disclosure, cancellation and deletion

As of commit 9fcac3a, identified as version 4.6.51 by the source, this FastAPI Jobs API remained unpatched. PraisonAI 4.6.34 fixed only the legacy Flask file src/praisonai/api_server.py for CVE-2026-44338 by adding AUTH_ENABLED, AUTH_TOKEN and check_auth().

The authoritative metrics for that distinct sibling are CVSS 7.3 HIGH, NOT in CISA KEV, EPSS 29%, CWE-306 (Missing Authentication for Critical Function) and CWE-668 (Exposure of Resource to Wrong Sphere). Those metrics and classifications apply to CVE-2026-44338, not CVE-2026-55539; no verified CVSS, CWE or KEV state was supplied for the present Jobs API issue.

Related advisory records describe separate PraisonAI exposure paths: an ignored --api-key on direct /agents invocation routes and unrestricted MCP file-read handlers. These are not confirmed stages of this attack chain and do not independently corroborate exploitation of /api/v1/runs.

No threat actor is named, and no attribution or in-the-wild exploitation is confirmed. The core evidence is single-sourced to the GitHub advisory and its submitted proof of concept.

4. Mitigation & containment

P1 — within 24 hours

  • Inventory every deployment running praisonai.jobs.server:create_app. Record the listening address, actual port, container publication, reverse-proxy route and tunnel exposure.
  • Test reachability from each untrusted network zone using an approved account and host:

bash curl -sS http://TARGET:8005/api/v1/runs

An unauthenticated HTTP 200 response indicates exposure. Do not exercise submit, cancel or delete operations against production data.

  • Disable the Jobs API where it is not required. Otherwise remove public port publication and bind it to loopback:

bash python -m uvicorn praisonai.jobs.server:create_app --host 127.0.0.1 --port 8005 --factory

Restrict the actual service port to explicitly authorised systems. Port 8005 is the source’s example and may differ locally.

  • Do not treat an upgrade to 4.6.34 or 4.6.51 as remediation for this issue. Version 4.6.34 fixed only CVE-2026-44338’s legacy Flask path; the source reports the Jobs API as vulnerable at 4.6.51.
  • If the service was reachable, preserve reverse-proxy, application and volatile job-state evidence before restarting it. Review successful requests to the affected routes and correlate them with LLM-provider usage and billing. Rotate or revoke provider credentials if evidence shows unauthorised use; credential rotation alone does not close the endpoint.

P2 — within 72 hours

  • Until an official fix is available, enforce authentication before every /api/v1/runs route. Prefer a router-level dependency so future child routes inherit the control. The source proposes PRAISONAI_JOBS_API_TOKEN, bearer or X-API-Key extraction and constant-time comparison:

```python import hmac, os from fastapi import Depends, Header, HTTPException

def verify_jobs_token( authorization: str | None = Header(None), x_api_key: str | None = Header(None, alias="X-API-Key"), ): expected = os.getenv("PRAISONAI_JOBS_API_TOKEN") if not expected: raise HTTPException(401, "Jobs API auth is not configured")

  token = x_api_key
  if authorization and authorization.startswith("Bearer "):
      token = authorization[7:]

  if not token or not hmac.compare_digest(token, expected):
      raise HTTPException(401, "Unauthorized")

```

Apply it to the router:

python APIRouter( prefix="/api/v1/runs", dependencies=[Depends(verify_jobs_token)], )

This is a proposed compensating change, not a vendor-released patch; test it before production deployment.

  • Introduce owner or principal binding for every job. Authentication alone does not prevent one authenticated caller from listing, reading, cancelling or deleting another caller’s jobs.
  • Add regression tests confirming unauthenticated submit, list, status, result, stream, cancel and delete requests return HTTP 401. Test incorrect bearer tokens, incorrect X-API-Key values and empty bearer values.

P3 — within seven days

  • Monitor the primary advisory for a confirmed fixed release. Before deployment, verify that the change modifies praisonai/jobs/, not only src/praisonai/api_server.py.
  • Conduct a retrospective review of /api/v1/runs access, source addresses, response codes, job identifiers, cancellation/deletion events and provider consumption. The supplied sources contain no known-malicious addresses or hashes against which to pivot.
  • Review the separate praisonai serve agents --api-key authentication-bypass and MCP file-read advisories as distinct exposure-management workstreams.

5. Indicators of compromise

No indicators of compromise available in the source material.

The following behaviours are exploitation-compatible but do not independently establish malicious activity. The evidence is single-sourced; verify before enforcement.

Behavioural indicators

Behaviour Where to observe Confidence
POST /api/v1/runs succeeds with HTTP 202 without an Authorization header, cookie or token Reverse-proxy/API access logs containing headers and status codes; FastAPI access logs High for vulnerability validation; low specificity for compromise
GET /api/v1/runs or GET /api/v1/runs/{job_id}/result returns HTTP 200 without credentials Reverse-proxy and application logs High for vulnerability validation; low specificity for compromise
POST /api/v1/runs/{job_id}/cancel returns HTTP 200, or DELETE /api/v1/runs/{job_id} returns HTTP 204, without credentials Reverse-proxy and application logs; job-state records High for the supplied proof of concept; caller intent requires investigation
Unexpected LLM-provider activity corresponding to externally submitted jobs PraisonAI executor telemetry and LLM-provider usage or billing records Medium; the source traced this path but used a stubbed agent during dynamic testing

6. Detection

The YARA rule below is suitable only for raw, decrypted HTTP request data or exported request content. It cannot establish that authentication was absent and may match legitimate clients or reporting that contains the same request strings.

rule PraisonAI_Jobs_API_Sensitive_HTTP_Operations
{
    meta:
        author = "Adverse Trace"
        date = "2026-08-25"
        reference = "https://github.com/advisories/GHSA-2jgc-f764-c5r2"
        description = "Detects result, cancel, or delete request text under /api/v1/runs"

    strings:
        $get_base = "GET /api/v1/runs/" ascii
        $post_base = "POST /api/v1/runs/" ascii
        $delete_base = "DELETE /api/v1/runs/" ascii
        $result = "/result" ascii
        $cancel = "/cancel" ascii

    condition:
        ($get_base and $result) or
        ($post_base and $cancel) or
        $delete_base
}

The following generic Sigma rule requires local field mapping. It intentionally detects successful sensitive operations regardless of whether an Authorization header was supplied because the vulnerable implementation ignores all credentials, including incorrect or empty values.

title: Successful PraisonAI Jobs API Operations
status: experimental
description: Detects successful submit, list, result, cancel, and delete operations against /api/v1/runs
author: Adverse Trace
date: 2026-08-25
references:

  - https://github.com/advisories/GHSA-2jgc-f764-c5r2
logsource:
  category: webserver
detection:
  selection_submit:
    http_method: POST
    url_path: /api/v1/runs
    status: 202
  selection_list:
    http_method: GET
    url_path: /api/v1/runs
    status: 200
  selection_result:
    http_method: GET
    url_path|startswith: /api/v1/runs/
    url_path|endswith: /result
    status: 200
  selection_cancel:
    http_method: POST
    url_path|startswith: /api/v1/runs/
    url_path|endswith: /cancel
    status: 200
  selection_delete:
    http_method: DELETE
    url_path|startswith: /api/v1/runs/
    status: 204
  condition: 1 of selection_*
falsepositives:

  - Legitimate internal Jobs API clients

Scope this detection to PraisonAI service addresses and investigate the caller, exposure path, associated job identifier and provider activity before enforcement.

CVE assessment

1 referenced CVE

CVE CVSS Exploited EPSS Summary
CVE-2026-44338 7.3 High 29% PraisonAI is a multi-agent teams system. From version 2.5.6 to before version 4.6.34, PraisonAI ships a legacy Flask API server…

7. Sources

  • GitHub Security Advisories, “PraisonAI: [Auth Bypass] PraisonAI async Jobs API (/api/v1/runs) has no authentication — unauthenticated job execution, result theft, cancel and delete,” https://github.com/advisories/GHSA-2jgc-f764-c5r2, 2026-08-25.
  • GitHub Security Advisories, “PraisonAI serve agents --api-key is ignored, allowing unauthenticated remote agent execution,” https://github.com/advisories/GHSA-7ww9-85pg-cv4x, date not provided in supplied material.
  • GitHub Security Advisories, “PraisonAI: [Auth Bypass] praisonai serve agents --api-key is silently ignored,” https://github.com/advisories/GHSA-r7v3-x45f-g7hp, date not provided in supplied material.
  • GitHub Security Advisories, “PraisonAI vulnerable to unauthenticated arbitrary file read via MCP workflow.show, workflow.validate, deploy.validate,” https://github.com/advisories/GHSA-9cr9-25q5-8prj, date not provided in supplied material.

8. Adverse Trace position

Adverse Trace does not assign a severity or CISA KEV state to CVE-2026-55539 because neither is present in the supplied authoritative data. Client impact is exposure-dependent: a loopback-only instance is not remotely reachable, while an instance published beyond the host permits pre-authentication job execution, cross-job result access and workload interference. CVE-2026-44338 is a distinct legacy sibling rated CVSS 7.3 HIGH, NOT in CISA KEV, with EPSS 29% and classifications CWE-306 and CWE-668; its 4.6.34 remediation does not protect the Jobs API. No live exploitation or actor attribution is confirmed, and the primary evidence is single-sourced; verify before enforcement. Adverse Trace will monitor the advisory for a fixed version, verified scoring and evidence of exploitation.


Read the original source →

Published via PulseTrace — Adverse Trace threat intelligence.

Post this to LinkedIn
Formatting is converted automatically — headings, bullets, a link back & hashtags. Paste straight in.
J
Jeff Davies