[Bug]: Arbitrary code execution through same-basename YAML sidecar of a checkpoint (model.target)
Checklist
- The issue exists after disabling all extensions
- The issue exists on a clean installation of webui
- The issue is caused by an extension, but I believe it is caused by a bug in the webui
- The issue exists in the current version of the webui
- The issue has not been reported before recently
- The issue has been reported before but has not been fixed yet
What happened?
stable-diffusion-webui automatically adopts a same-basename .yaml file sitting next to a .safetensors checkpoint as the model configuration (modules/sd_models_config.py, find_checkpoint_config_near_filename, lines 128-136). The YAML is parsed with OmegaConf and its model.target string is resolved to a Python object with importlib.import_module() + getattr() and invoked with model.params as kwargs (modules/sd_models.py:767-783, called from load_model at lines 809-820).
A model package containing only three ordinary files therefore executes arbitrary attacker-supplied Python as soon as the victim selects the checkpoint in the UI:
mbe2e_pack/
model.safetensors (inert, written with the official safetensors writer API)
model.yaml model.target: torch.hub.load
hubconf.py arbitrary top-level Python, imported by torch.hubwith model.yaml:
model:
target: torch.hub.load
params:
repo_or_dir: models/Stable-diffusion/mbe2e_pack
model: build
source: localSelecting model.safetensors makes instantiate_from_config call torch.hub.load(repo_or_dir=..., model="build", source="local", use_ema=False), and torch.hub imports the package's hubconf.py, running its top-level code inside the webui process. The injected use_ema=False (repair_config, sd_models.py:599-601) is absorbed by torch.hub.load's **kwargs forwarding, so this gadget survives the real product path.
I verified this end-to-end on a clean checkout of commit 82a973c04367123ae98bd9abdf80d9eda9b910e2 (the v1.10.1 release commit): a negative-control package whose model.yaml only differs in model.target: torch.nn.Identity (same hubconf.py on disk) does NOT execute hubconf.py, while the package with target: torch.hub.load writes a canary file from hubconf.py. The product's own API even reports the attacker's YAML as the model config: GET /sdapi/v1/sd-models shows "config": ".../models/Stable-diffusion/mbe2e_pack/model.yaml".
Steps to reproduce the problem
- Unpack the attacker's package into models/Stable-diffusion/: mbe2e_pack/model.safetensors (any valid tiny safetensors file), mbe2e_pack/model.yaml (target: torch.hub.load, repo_or_dir pointing at the package dir, model: build, source: local), mbe2e_pack/hubconf.py (top-level code that writes a canary file, plus a build(**kwargs) entry point).
- Start the webui normally (my run: --api --skip-load-model-at-start --use-cpu all; the code path is identical when selecting a checkpoint from the UI dropdown).
- Select the checkpoint "mbe2e_pack/model.safetensors" (UI dropdown, or POST /sdapi/v1/options {"sd_model_checkpoint": "mbe2e_pack/model.safetensors"}).
- Observe the canary file created by hubconf.py inside the webui process, and the console line "Creating model from config: .../mbe2e_pack/model.yaml".
- Negative control: an otherwise identical package with
model.target: torch.nn.Identityandparams: {}loads the same sidecar (same console line) but does NOT run hubconf.py — proving the execution is driven by the YAML'stargetfield.
What should have happened?
Files packaged next to a downloaded checkpoint must not be executed as code. The sidecar config should either be restricted to a validated, allow-listed schema (no arbitrary model.target object resolution), or same-basename YAMLs from untrusted model archives should be ignored/warned about. The dynamic import path (get_obj_from_str -> importlib.import_module + getattr, sd_models.py:778) should not be reachable from user-supplied config files.
What browsers do you use to access the UI ?
Google Chrome
Sysinfo
Console logs
Calculating sha256 for /home/<user>/mbe2e/a1111-yaml-target/stable-diffusion-webui/models/Stable-diffusion/mbe2e_neg/model.safetensors: e2aa1d1ef36ae74397455e34469d45f9b025a92c2784931ebd8738d5a49f46ad
Loading weights [e2aa1d1ef3] from /home/<user>/mbe2e/a1111-yaml-target/stable-diffusion-webui/models/Stable-diffusion/mbe2e_neg/model.safetensors
Creating model from config: /home/<user>/mbe2e/a1111-yaml-target/stable-diffusion-webui/models/Stable-diffusion/mbe2e_neg/model.yaml
Calculating sha256 for /home/<user>/mbe2e/a1111-yaml-target/stable-diffusion-webui/models/Stable-diffusion/mbe2e_pack/model.safetensors: d5f1f2669119094351dcd419f93e40bec964ff6a08223a0f6ad1598cf34e5133
Loading weights [d5f1f26691] from /home/<user>/mbe2e/a1111-yaml-target/stable-diffusion-webui/models/Stable-diffusion/mbe2e_pack/model.safetensors
Creating model from config: /home/<user>/mbe2e/a1111-yaml-target/stable-diffusion-webui/models/Stable-diffusion/mbe2e_pack/model.yaml
# after the second selection, the canary file written by hubconf.py exists:
$ ls -l /out
total 4
-rw-r--r-- 1 <user> <user> 34 Sep 29 08:29 pwned_by_a1111_yaml_target
$ cat /out/pwned_by_a1111_yaml_target
MBE2E-CANARY-a1111-yaml-target-v1Additional information
- Affected: v1.10.1 (release commit 82a973c04367123ae98bd9abdf80d9eda9b910e2) and current master HEAD (same commit). Verified on Linux (Python 3.11, torch 2.1.2+cpu); the vulnerable code paths are platform-independent Python.
- Vulnerable code: modules/sd_models_config.py:128-136 (same-basename sidecar discovery); modules/sd_models.py:809-820 (OmegaConf.load -> instantiate_from_config); modules/sd_models.py:767-783 (target string -> importlib + getattr -> call); modules/sd_models.py:599-601 (repair_config injects use_ema=False)
- Suggested severity: High — CVSS 3.1 AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H (8.8); CWE-470 / CWE-94. Model packages are routinely downloaded from third-party sites and unpacked into models/Stable-diffusion/, so "unpack + select" is the standard victim flow.
- The safetensors file itself is inert here (official writer API); the flaw is the automatic adoption of the sidecar YAML plus unrestricted target instantiation.
- Suggested fix: validate sidecar configs against a fixed schema with an allow-list for model.target, or only honor explicitly registered configs; do not dynamically import attacker-named modules from checkpoint-adjacent files.
- Attached: a1111-yaml-target-poc.zip containing the positive package (mbe2e_pack/), the negative-control package (mbe2e_neg/), the generator script (make_poc.py), SHA256SUMS.txt, a step-by-step REPRODUCE.md and the 12 evidence screenshots (screenshots/). The canary path /out/pwned_by_a1111_yaml_target in hubconf.py is an inert marker file write used to demonstrate execution.
Source: AUTOMATIC1111/stable-diffusion-webui