This is a booby-trapped package on the loading dock, not a burglar kicking in your front door
CVE-2026-4372 is a model-loading RCE in huggingface/transformers where a malicious model config.json can smuggle an internal field such as _attn_implementation_internal that points model loading toward attacker-controlled code. NVD currently says all versions before 5.3.0 are affected, while multiple secondary reports narrow practical exploitability to the newer 4.56.0 through 5.2.x line and sometimes mention the optional kernels path; absent a vendor advisory clarifying the scope, treat <5.3.0 as the safe operational boundary.
The vendor's 7.8 HIGH is directionally fair on raw impact but too generous on reachable population. This is not an unauthenticated network service bug; the attacker needs a victim workflow that fetches or opens an untrusted model, which means exposure is concentrated in ML workstations, CI jobs, GPU servers, and automated evaluation pipelines rather than your whole estate. The bypass of trust_remote_code=False is the amplifier, but the need to load hostile content is still the main friction, so this lands as MEDIUM for broad enterprise patch triage.
4 steps from start to impact.
Seed a malicious model repo on the Hub
config.json carries a hostile internal kernel-selection value such as _attn_implementation_internal. The weaponized tool here is the Hugging Face Hub itself as the distribution channel, with the payload hidden in a normal-looking model artifact rather than a standalone executable.- Attacker can publish or socially engineer use of a model repository
- Victim organization allows staff or automation to pull models from public or weakly governed sources
- Private allowlists of trusted publishers cut this off early
- Many enterprises do not let production jobs pull arbitrary public models
- The attacker still needs visibility or lure value so someone actually chooses the model
config.json for suspicious internal fields.Victim loads the model in a normal workflow
AutoModelForCausalLM.from_pretrained() against the malicious repo. The important operational twist is that this can occur during routine benchmarking, fine-tuning, notebook experimentation, or automated model evaluation, so the exploit rides on expected developer behavior.- A vulnerable
transformersversion is installed - A model-loading workflow fetches the attacker-controlled repo
- User interaction or pipeline execution occurs
- Requires a real model-load event, not just package presence
- Some environments pin models internally and never hit public Hub content
- Air-gapped or mirrored environments reduce spontaneous exposure
Internal field bypasses expected safety assumptions
a7f8e7f. The weaponized component is src/transformers/integrations/hub_kernels.py, where the fix explicitly limits acceptable kernel repos and blocks deserialization of problematic internal fields.- The runtime honors the malicious internal config field
- The vulnerable integration path is reachable in the installed build
- If the runtime never reaches kernel-loading logic, practical exploitability may be lower
- Reports disagree on whether older versions or only newer branches are truly weaponizable
_attn_implementation_internal or _experts_implementation_internal in cached Hub configs add better signal.Arbitrary code runs with the loader's OS privileges
- Successful code retrieval and execution from the malicious repo
- Victim process has access to secrets or adjacent infrastructure
- Least-privilege service accounts and ephemeral runners can contain impact
- Container sandboxes and egress controls can limit follow-on actions
- This does not automatically grant enterprise-wide control
The supporting signals.
| In-the-wild status | No confirmed in-the-wild exploitation found in the sources reviewed, and the user-provided intel says KEV listed: No. |
|---|---|
| KEV status | Not present in CISA KEV as of the reviewed catalog page; this matches the user intel. |
| PoC / exploit path | A public exploit path is effectively embedded in the advisory narrative itself: malicious config.json plus AutoModelForCausalLM.from_pretrained(). NVD references a Huntr advisory and the patch commit; that is enough for competent operators to reproduce quickly. |
| EPSS | 0.00089 from the user intel, which is very low and consistent with a niche, workflow-dependent package exploit rather than mass internet exploitation. |
| CVSS vector reality check | CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H correctly signals user interaction and a non-network attack path. In plain English: devastating once triggered, but not directly wormable over the internet. |
| Affected versions | NVD says all versions before 5.3.0. Secondary reporting suggests the weaponized path may be concentrated in 4.56.0 through 5.2.x and in some writeups only when the optional kernels path is present; treat that narrower range as an inference from secondary sources, not the authoritative floor. |
| Fixed version | 5.3.0 according to NVD and multiple downstream databases. The referenced patch commit is a7f8e7ff37d87d1a1a0c8cf607971c607741452f. |
| Exposure / scanning reality | Shodan/Censys/FOFA/GreyNoise are poor lenses here because this is a client/library flaw, not an internet-exposed daemon with a banner to fingerprint. Exposure has to be measured through asset inventory, SBOM, package managers, containers, notebooks, and CI runners. |
| Disclosure timeline | User intel gives 2026-05-24 disclosure. NVD change history shows the CVE was received from huntr.dev on 2026-05-24 and NIST added enrichment on 2026-06-04. |
| Researcher / reporting org | The CNA on NVD is huntr.dev. A precise individual researcher name was not recoverable from the browsed Huntr page rendering. |
noisgate verdict.
The decisive factor is attacker position: this bug requires the victim to load a malicious model or config into a vulnerable ML workflow, so it is a supply-chain execution path rather than an unauthenticated remote service exploit. That sharply narrows the exposed population even though the host-level impact after trigger is severe.
Why this verdict
- Downgrade for attacker position: this is not unauthenticated remote against a listening service; it requires a victim model-load event, which implies prior lure success or a permissive automation pipeline.
- Downgrade for exposed population: only hosts that both run
transformersand ingest untrusted model repos are materially at risk. On a 10,000-host estate, that is usually a small ML subset, not the fleet. - Upgrade from LOW because trust assumptions are broken: the bypass of
trust_remote_code=Falsemeans normal defensive habits around 'safe model loading' are not sufficient, so impacted ML platforms deserve real urgency. - Downgrade for current threat intel: no KEV listing, no confirmed active exploitation, and very low EPSS mean the external exploitation pressure is currently weak.
- Keep it above backlog noise because blast radius is rich: the vulnerable hosts are often GPU servers, CI runners, or data-science workstations with secrets, cloud creds, source code, and proprietary datasets.
Why not higher?
It is not higher because the exploit chain is gated by user or pipeline behavior: somebody has to load a malicious model, and many enterprises already restrict public model intake. There is also no strong evidence yet of active exploitation or mass scanning pressure, which matters when deciding what jumps the queue for an enterprise patch calendar.
Why not lower?
It is not lower because the end state is still arbitrary code execution on high-value AI infrastructure under routine documented usage. The trust_remote_code bypass removes a safety assumption many teams were relying on, so affected ML assets should not be treated as mere hygiene.
What to do — in priority order.
- Block untrusted model sources — Enforce publisher allowlists or internal mirrors for Hugging Face content and stop direct pulls from arbitrary public repos. For a MEDIUM verdict there is no mitigation SLA, but if these systems ingest public models today, deploy this control immediately as risk reduction while you move toward the patch.
- Scan caches for hostile internal fields — Search downloaded or cached model
config.jsonfiles for_attn_implementation_internaland_experts_implementation_internalto find likely exposure or tampering. There is no mitigation SLA — go straight to the 365-day remediation window, but do the scan early because it can uncover already-seeded artifacts. - Constrain model-loading runtimes — Run notebooks, CI jobs, and model-eval workers with least-privilege identities, restricted egress, read-only filesystems where possible, and short-lived credentials. This does not remove the bug, but it limits the damage if code execution fires before you fully remediate.
- Instrument EDR on GPU and ML nodes — Make sure the hosts that actually load models have runtime telemetry and alerting for shell spawns, network fetches, and token access from Python processes. This is especially useful on research and evaluation infrastructure where package scanners alone have little visibility.
trust_remote_code=Falseby itself does not solve this; the vulnerability explicitly abuses a path that bypasses that safety expectation.- Perimeter network scanning does not help much because this is not mainly an internet-exposed service fingerprinting problem.
- Patching only developer laptops while leaving CI runners and GPU workers untouched misses the systems most likely to auto-load models at scale.
Crowdsourced verification payload.
Run this on the target host, container image, notebook base image, or CI runner that may have transformers installed. Invoke it with python3 verify_cve_2026_4372.py or point it at an alternate cache root with HF_HOME=/srv/hf-cache python3 verify_cve_2026_4372.py; no admin rights are required, but the script only inspects locations the current user can read.
#!/usr/bin/env python3
"""
verify_cve_2026_4372.py
Checks for vulnerable Hugging Face transformers versions and scans common
Hugging Face cache locations for suspicious internal config fields related to
CVE-2026-4372.
Outputs one of:
VULNERABLE - vulnerable version present and/or suspicious cached configs found
PATCHED - transformers installed at >= 5.3.0 and no suspicious configs found
UNKNOWN - transformers not installed, version unreadable, or no readable evidence
Exit codes:
0 = PATCHED
1 = VULNERABLE
2 = UNKNOWN
"""
import json
import os
import re
import sys
from pathlib import Path
TARGET_VERSION = (5, 3, 0)
SUSPICIOUS_KEYS = {"_attn_implementation_internal", "_experts_implementation_internal"}
def parse_version(v):
nums = re.findall(r"\d+", str(v))
if len(nums) < 3:
nums += ["0"] * (3 - len(nums))
try:
return tuple(int(x) for x in nums[:3])
except Exception:
return None
def get_transformers_version():
try:
try:
from importlib.metadata import version, PackageNotFoundError
except ImportError:
from importlib_metadata import version, PackageNotFoundError # type: ignore
try:
return version("transformers")
except PackageNotFoundError:
return None
except Exception:
return None
def candidate_cache_roots():
roots = []
env_hf_home = os.environ.get("HF_HOME")
env_hub_cache = os.environ.get("HUGGINGFACE_HUB_CACHE")
home = Path.home()
if env_hub_cache:
roots.append(Path(env_hub_cache))
if env_hf_home:
roots.append(Path(env_hf_home) / "hub")
roots.extend([
home / ".cache" / "huggingface" / "hub",
home / ".cache" / "huggingface",
Path("/root/.cache/huggingface/hub"),
Path("/opt/huggingface/hub"),
Path("/var/cache/huggingface/hub"),
])
# de-duplicate while preserving order
seen = set()
out = []
for r in roots:
s = str(r)
if s not in seen:
seen.add(s)
out.append(r)
return out
def scan_for_suspicious_configs():
hits = []
scanned_files = 0
for root in candidate_cache_roots():
if not root.exists():
continue
for path in root.rglob("config.json"):
try:
scanned_files += 1
with path.open("r", encoding="utf-8", errors="ignore") as f:
data = json.load(f)
bad = sorted(SUSPICIOUS_KEYS.intersection(set(data.keys())))
if bad:
hits.append({"path": str(path), "keys": bad})
except Exception:
continue
return scanned_files, hits
def main():
version_str = get_transformers_version()
version_tuple = parse_version(version_str) if version_str else None
scanned_files, hits = scan_for_suspicious_configs()
print("=== CVE-2026-4372 verification ===")
print(f"transformers_version={version_str}")
print(f"scanned_config_files={scanned_files}")
print(f"suspicious_config_hits={len(hits)}")
for hit in hits[:20]:
print(f"hit={hit['path']} keys={','.join(hit['keys'])}")
if len(hits) > 20:
print(f"note=truncated_output total_hits={len(hits)}")
# Decision logic
if version_tuple is None and scanned_files == 0:
print("UNKNOWN")
return 2
if version_tuple is not None and version_tuple < TARGET_VERSION:
print("VULNERABLE")
return 1
if hits:
# Even if patched now, suspicious cached artifacts suggest prior exposure risk.
print("VULNERABLE")
return 1
if version_tuple is not None and version_tuple >= TARGET_VERSION:
print("PATCHED")
return 0
print("UNKNOWN")
return 2
if __name__ == "__main__":
sys.exit(main())
If you remember one thing.
transformers runs: notebooks, shared GPU servers, build agents, model-eval workers, and container bases. Because this verdict is MEDIUM, there is no noisgate mitigation SLA — go straight to the 365-day remediation window for the patch itself, but do not wait to apply commonsense guardrails: immediately restrict public model intake on the affected ML subset and scan caches for suspicious config fields. For formal timing, use the noisgate mitigation SLA statement above and complete patching to 5.3.0 or later within the noisgate remediation SLA of 365 days; if your environment auto-loads public Hub models in CI or production, compress that schedule aggressively and treat those systems as local priority exceptions.Sources
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.