Products Lemma APIProof issuance & verification platformTrust402Delegate to agents, and transactSealSign-in for the agent era — no keys handed over
Use cases Manufacturing & Critical InfraInspection Record AssuranceFinance & FinTechCounterparty Record VerificationPublic SectorCertificate-less ProceduresHealthcareQualified Worker AssuranceProcurement & Supply ChainSupplier Credential MonitoringMedia & ContentContent AuthenticityService & RetailCross-group IdentityAI Adoption (cross-industry)AI Run GovernanceDevelopers & Agent OpsAgent Authority Control ▸ Browse the use-case index
Pricing
Resources Critical BriefThe frontier of AI × trustBlogThinking and implementation notesDocumentationAPI & specsVerification CenterReal verification & issuance countsAbout usFRAME00, Inc.ContactSales & press inquiriesNewsletterUpdates by emailGlossaryDefinitionsFAQFrequently asked questions
Get Started ↗ JA
Home / Critical Brief / No. 121

"FaceHugger" in Hugging Face Diffusers: loading a model ran arbitrary code

the safeguard only checked the first fetch (Zafran / CVE-2026-44827 et al.)

Incident date
2026-07-27
Published
2026-08-04
Authors
Lemma Critical Team
Related Pack
Pack A · Incident Response

TL;DR

On July 27, 2026, security firm Zafran disclosed a set of flaws — collectively named FaceHugger — in Hugging Face Diffusers that let a malicious model repository run arbitrary Python on a user’s machine at load time. The cause was a time-of-check-to-time-of-use (TOCTOU) gap: model downloads split into two non-atomic HTTP requests, and the trust_remote_code safeguard ran against only the first. The guard was in place and running. What was missing is the layer that confirms that the artifact the guard checked is the artifact that actually runs.

What happened

  • The target is Diffusers, Hugging Face’s diffusion-model library. Anyone calling DiffusionPipeline.from_pretrained with custom pipelines is affected; loading a malicious model repository quietly executes attacker code. The library draws roughly seven million downloads a month — close to 200,000 a day — sitting inside production AI pipelines and CI/CD systems.
  • Three CVEs track the issue. CVE-2026-44827 (CVSS 8.8) — code injection via the default-resolved filename None.py loaded as custom pipeline code. CVE-2026-45804 (CVSS 7.5) — a race condition between the hf_hub_download and snapshot_download calls, with the configuration rewritten in the interval between them. CVE-2026-44513 (CVSS 8.8) — three further variants sharing the same root cause: cross-repository pipeline loading, loads from a local snapshot, and malicious custom components.
  • All of them bypass trust_remote_code — the safeguard built to stop unreviewed custom code from executing, which is off by default (False).

The bypass works through the following chain.

  1. A model download is split into a config fetch and a body fetch — two HTTP requests rather than one atomic operation.
  2. The trust_remote_code check applies only to the first request, the config.
  3. The attacker makes what the guard inspected diverge from what actually loads. In the race-condition variant (CVE-2026-45804) the config is swapped in the window between the check and the body fetch — Zafran measured that window at roughly 0.3 seconds, and the exploit needs an uncached first download. Even so, Zafran notes that on a heavily used repository an attacker can still win the race often enough, by briefly publishing a malicious config and then reverting it. The other variants need no timing at all: they create the same divergence through the default resolution of None.py, or through cross-repository and local-snapshot loads.
  4. The substituted code runs with the “safe” status the guard already conferred.

Timeline — disclosure and response

  • 2026-03-19: Zafran reports the first two flaws to Hugging Face.
  • 2026-05-01: Diffusers 0.38.0 ships with the fix.
  • 2026-05: the CVEs are published following responsible disclosure.
  • 2026-07-27: Zafran Labs (Gal Zaban, Ido Shani) publishes the technical detail.
  • 2026-08-03: The Hacker News, Cybersecurity News and others report on it, and coverage spreads.

All three CVSS scores are the assessment of the CNA, GitHub. NVD’s own evaluation status differs by CVE: as of writing, CVE-2026-44827 is Analyzed, CVE-2026-44513 is Modified, and CVE-2026-45804 is Awaiting Analysis. This was responsible disclosure; as of publication no widespread in-the-wild exploitation has been reported.

The response and industry movement after disclosure:

  • Diffusers 0.38.0 relocated the security checks to the dynamic-module loading chokepoint, so that what is checked and what is executed are the same object, closing the known bypass paths. Users are advised to upgrade and to pin repository revisions.
  • Zafran also disclosed a parallel flaw in Hugging Face’s transformers, where a pinned commit hash fails to propagate, letting an attacker swap in malicious code after trust_remote_code approval — the same check-then-use gap.
  • Comparable load-time code execution had already surfaced in other frameworks in March and April 2026, ahead of this disclosure: an equivalent trust_remote_code bypass in vLLM in March, and InstructLab hardcoding trust_remote_code=True in April. “Loading a model can execute code” is not specific to diffusion models.

Why it wasn’t stopped

The failure here is neither a missing safeguard nor a malfunctioning one. There was no layer that independently confirmed that what the guard checked and what actually executes are the same thing.

trust_remote_code exists to stop unreviewed code from running, and it refused custom code by default. Detection worked. What was missing came earlier — any guarantee that the artifact seen at the moment of the check is the artifact executed at the moment of the load.

TOCTOU makes a check meaningless whenever the target can change between check and use. The guard does not lie. The thing it checked simply no longer exists.

Models ship wrapped in a name, a card, and a download count. None of those prove the provenance of the code about to run. The substitution happens inside the “trusted” distribution path itself. This pushes the lesson of Brief 116 — trending and download counts are not a substitute for provenance — all the way to integrity at the moment of execution, and it is continuous with the timing gap in Brief 090, where the contents change after passing a scanner.

What proof would have changed

Proof-as-auth inserts one layer into the path ahead of each individual load of a model: an independent verification of the provenance of the artifact that is about to execute. Rather than treating a repository’s location or reputation as a stand-in for provenance, it establishes — before execution can proceed — whether this code and these weights were issued by the publisher they claim, unaltered. The placement is the point: not in the window between check and execution where the swap happens, but immediately before execution.

Lemma’s design against this primitive:

  • Bind provenance to the executing artifact. Attach provenance and issuer proof to the hash of the code and weights that actually load — not to the model card or the name.
  • Verify immediately before loading. After the fetch completes and before execution begins, confirm the artifact matches verified provenance, closing the gap between check and use.
  • Verify the issuer independently. Confirm the publisher’s identity independently of where the repository sits.
  • Execute with least privilege. Confine the model-loading process to a scope that does not presume code execution.

Lemma is not a product that tells good models from bad, nor one that judges whether code is dangerous. Its scope is to verify the provenance of the executing artifact before it runs, and to hold back loads whose artifact does not match its proof. Scanners and guards (trust_remote_code, revision pinning, repository monitoring) and pre-execution proof (an audit trail confirming artifact identity immediately before execution) are complementary, not alternatives. The first rejects known danger; the second closes the one thing detection structurally cannot reach — the drift between what was checked and what runs. For the complementarity framing see “The last layer left for cyber defense in the age of AI” (Lemma, 2026-05); for design detail, “Proof-as-Auth: sign in without ever sending your key”; for scope, Pillar 02 — Verifiable AI.

Sources

“The last layer left for cyber defense in the age of AI”Pillar 02 — Verifiable AIBrief 116 (fake OSS privacy filter)Brief 090 (AIR fake agent skill)

This material is a structured analysis of public information; it is not an audit, diagnosis, or recommendation for any specific organization.

Cite this Brief

Lemma Critical Team. (2026).
""FaceHugger" in Hugging Face Diffusers: loading a model ran arbitrary code — the safeguard only checked the first fetch (Zafran / CVE-2026-44827 et al.)".
Lemma Critical Brief No.121. Lemma / FRAME00, Inc.
https://lemma.frame00.com/critical/briefs/121-hugging-face-diffusers-toctou/
Lemma

If it can't be verified,
it doesn't enter your operation.

Lemma attaches cryptographic proofs to data and AI execution, so the receiving side can confirm authenticity without asking the issuer. Detection stays; a proof layer is added in front of it.