[Bug]: Arbitrary code execution through same-basename YAML sidecar of a checkpoint (model.target)

Author: 3em0Created Sep 30, 2026Updated Sep 30, 2026
Labelsbug-report

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.hub

with model.yaml:

model:
  target: torch.hub.load
  params:
    repo_or_dir: models/Stable-diffusion/mbe2e_pack
    model: build
    source: local

Selecting 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

  1. 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).
  2. 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).
  3. Select the checkpoint "mbe2e_pack/model.safetensors" (UI dropdown, or POST /sdapi/v1/options {"sd_model_checkpoint": "mbe2e_pack/model.safetensors"}).
  4. Observe the canary file created by hubconf.py inside the webui process, and the console line "Creating model from config: .../mbe2e_pack/model.yaml".
  5. Negative control: an otherwise identical package with model.target: torch.nn.Identity and params: {} loads the same sidecar (same console line) but does NOT run hubconf.py — proving the execution is driven by the YAML's target field.

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

sysinfo-2026-09-29-00-33.json

Console logs

bash
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-v1

Additional 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.

a1111-yaml-target-poc.zip

link: https://github.com/3em0/cve_repo/blob/main/2026/AUTOMATIC1111_stable-diffusion-webui_v1.10.1_Arbitrary_Code_Execution_via_Same-Basename_YAML_Sidecar(%60model.target%60).md

Source: AUTOMATIC1111/stable-diffusion-webui