Threat · curated 27 Jul 2026
Hugging Face Diffusers Flaws Could Let Model Repositories Execute Arbitrary Code
First reported · updated · 2 reports zafran.io
Coverage timeline
Why it matters
FaceHugger turns the widely used Diffusers library (~200,000 installs per day) into an AI supply-chain foothold, letting attackers gain code execution deep inside enterprise environments through what looks like an ordinary model download.
Zafran Labs disclosed three high-severity flaws, collectively named FaceHugger, in Hugging Face's Diffusers library (CVE-2026-44827 CVSS 8.8, CVE-2026-45804 CVSS 7.5, and CVE-2026-44513 CVSS 8.8) that let a malicious model repository silently execute arbitrary code on any machine loading it. The flaws stem from a Time-of-Check to Time-of-Use race that bypasses the trust_remote_code safeguard by splitting a model download into two non-atomic HTTP requests, so a routine model load becomes an initial-access vector across CI/CD, container, and production pipelines.
Summary
Zafran Labs, as part of its Project DarkSide research, disclosed a set of high-severity vulnerabilities in Hugging Face's diffusers library that let a malicious model repository silently execute arbitrary code on any client machine that loads it, bypassing the trust_remote_code safeguard designed to stop unreviewed custom pipeline code from running.[0]
Every variant traces to a single root cause: a Time-of-Check to Time-of-Use (TOCTOU) flaw in which a model download that should be atomic is split into two sequential, non-atomic HTTP requests, with the security gate enforcing trust_remote_code running only against the first request. The findings comprise CVE-2026-44827 (CVSS 8.8), a code-injection flaw; CVE-2026-45804 (CVSS 7.5), a race condition; and three related variants tracked under CVE-2026-44513 (CVSS 8.8), the latter reported by GitHub user Vancir.[0][2][3][4][5]
Because diffusers draws roughly 7 million downloads per month (~200,000 installs per day) and is embedded in production pipelines, CI/CD systems, and container images, a single compromised model load could hand an attacker initial access deep inside an enterprise environment. Zafran links the model-loading weakness to Hugging Face's July 2026 security incident, in which a malicious dataset abused code-execution paths to escalate to node-level access and move laterally across internal clusters.[0][1]
Attack chain
- None.py trust bypass (CVE-2026-44827): An attacker publishes a repository containing a file named None.py. When no custom_pipeline argument is supplied, the loader's _resolve_custom_pipeline_and_cls formats the lookup to None.py and detects it as a custom pipeline, while download's gate does not detect None.py as custom code, so the file is imported and executed. Aliasing a naive pipeline class in None.py keeps the pipeline functional and hides the execution.[0]
- TOCTOU race condition (CVE-2026-45804): download fetches the config file first via hf_hub_download and runs the trust check against it, then fetches the rest of the repository via a separate snapshot_download call. With revision=None each call resolves the default branch's current HEAD, so an attacker who modifies the config to point to custom code within the ~0.3 second window between the two calls achieves execution, statistically succeeding across a popular repo's many daily downloads.[0]
- Custom-pipeline and local-snapshot variants (CVE-2026-44513): Cross-repo custom_pipeline points the model to repoA while the pipeline is imported from attacker-controlled repoB, evading the gate that checks repoA's file list. When the primary path is a local directory, from_pretrained never calls download at all, so the gate is never reached, and a local snapshot's model_index.json referencing a custom component file causes that component code to be loaded and executed.[0]
Disclosure timeline
| Date | Event |
|---|---|
| March 19, 2026 | Zafran Labs reported the None.py Trust Remote Code Bypass and TOCTOU Trust Remote Code Bypass to Hugging Face.[0] |
| April 30, 2026 | Hugging Face released diffusers version 0.38.0 with the fixes.[0] |
| May 1-7, 2026 | Hugging Face acknowledged the findings and started the CVE assignment process.[0] |
| May 14, 2026 | CVE-2026-44827 published for the None.py Trust Remote Code Bypass.[0] |
| May 20, 2026 | CVE-2026-45804 published for the TOCTOU Trust Remote Code Bypass.[0] |
| July 16, 2026 | Hugging Face published disclosure of a July 2026 production infrastructure intrusion driven by an autonomous AI agent that abused dataset code-execution paths.[1] |
| July 27, 2026 | Zafran Labs published the FaceHugger research detailing the diffusers vulnerabilities.[0] |
How it works
from_pretrained calls DiffusionPipeline.download, which serves as the trust_remote_code gatekeeper: it reads the configuration, decides whether the repo requests custom code, and raises a ValueError if custom code is present without trust_remote_code=True. The root cause of all variants is that this trust check lives entirely in the first phase, so any method that makes the loader see custom code the gate did not see bypasses the safeguard.[0]
For CVE-2026-44827, when custom_pipeline defaults to None, the check f"{custom_pipeline}.py" resolves to None.py; if such a file exists it is detected and loaded as a custom pipeline by _resolve_custom_pipeline_and_cls, even though download's gate uses different string handling and never flags None.py. Placing an aliased subclass of a real pipeline (e.g. FluxPipeline) in None.py keeps the pipeline functional while running arbitrary attacker code.[0]
For CVE-2026-45804, download performs hf_hub_download for the config and runs the trust check against it, then later calls snapshot_download for the rest of the repo. These are two independent HTTP operations that each resolve the branch HEAD when revision=None, so modifying the config to point to custom code in the roughly 0.3 second window between the calls yields execution. If all files are already cached, download returns early and the race window closes, so a first or forced download is required.[0]
A parallel flaw exists in the transformers package: after the config fetch resolves and pins a commit hash, some from_pretrained flows do not propagate that hash into get_class_from_dynamic_module, which re-resolves the commit and can pull a newer, attacker-added commit between the config fetch and the module fetch. A warning about the version change is printed but the code executes anyway.[0]
Affected versions and patch status
| Product | Affected | Patch status |
|---|---|---|
| Hugging Face diffusers library | Versions prior to 0.38.0 | Fixed in 0.38.0 (released April 30, 2026), which moves security checks to the dynamic-module loading chokepoint and closes the bypass variants.[0] |
| Hugging Face transformers library | from_pretrained flows that fail to propagate the pinned commit hash into get_class_from_dynamic_module | Flaw disclosed and acknowledged by Hugging Face's security team; no specific fixed version stated in the evidence.[0] |
Key takeaways
- A single misplaced trust check that runs only against the first of two non-atomic HTTP requests reintroduces a classic TOCTOU class of bug into widely adopted AI infrastructure, letting a routine model download become arbitrary code execution.[0]
- trust_remote_code is not a sufficient safeguard on its own; the None.py, cross-repo, and local-snapshot variants each let attacker code execute without the gate ever detecting it.[0]
- Given diffusers' ~200,000 daily installs and embedding in production, CI/CD, and container environments, even a short exploitation window can be exploited statistically across a large user base, making prompt patching to 0.38.0 and revision pinning essential.[0]
- The parallel transformers flaw shows the same commit-pinning weakness spans multiple Hugging Face libraries, and a trusted repo's reputation may discourage clients from auditing transient malicious commits.[0]
Defensive actions
- Upgrade the diffusers library to version 0.38.0 or later.: Version 0.38.0 moves the security checks to the dynamic-module loading chokepoint and closes the identified trust_remote_code bypass variants.[0]
- Pin specific model repository revisions when loading remote models.: Pinning a revision prevents the TOCTOU race window where a repo's HEAD can change between the config fetch and the file fetch, ensuring consistency and blocking statistical-success attacks.[0]
- Treat AI model repositories, configuration files, loaders, and custom pipeline code as untrusted executable code rather than passive data.: Artifacts from AI repositories can quietly cross into executable code, turning a routine model load into an initial-access vector, as demonstrated by both the diffusers flaws and Hugging Face's July 2026 incident.[0][1]