# One Bug Made the Cut: Lucent on ComfyUI

> Source: <https://blog.attacks.ai/lucent-on-comfyui>
> Published: 2026-08-11 00:00:00+00:00

I aimed the hunter from the last post at something bigger and more popular than [hermes-agent](https://github.com/NousResearch/hermes-agent): [ ComfyUI](https://github.com/Comfy-Org/ComfyUI), together with its companion

[. If you have not run it, ComfyUI is a node-graph editor for image and video generation: you wire nodes into a workflow (load a checkpoint, encode a prompt, sample, decode, save) and a local server executes the graph. It became the power-user's interface for Stable Diffusion and the open image and video models that followed, the tool people reach for when a single-box web UI runs out of room. The project carries tens of thousands of GitHub stars and a custom-node ecosystem in the thousands, and the team behind it has since spun out as Comfy Org. I have history with this one: I was CISO at](https://github.com/ltdrdata/ComfyUI-Manager)

**ComfyUI-Manager**[Stability AI](https://stability.ai), where ComfyUI sat in the stack I was responsible for, so pointing the hunter at it was personal as much as a stress test.

And the surface is wide. ComfyUI serves an unauthenticated HTTP API that executes user-submitted workflow graphs, it loads third-party "custom nodes" as Python, and the Manager can install packages, switch versions, and reboot the server. A lot for a bug-hunter to pull on.

The honest headline first: ComfyUI is well-built. At its defaults it holds, and the audit did not turn up a dropped remote-code-execution hole. The more useful result is about its threat model. ComfyUI assumes a single trusted operator on localhost, and under that assumption its defenses are consistent. The risk is in the things a user might assume are security boundaries and that are not. Multi-user mode is not authentication. The Manager's "security levels" allow more at the default than the name suggests. And nothing in the product stops you putting the whole thing on a network it was never designed to face.

A word on the tool, because it cuts against the last post. The cheap part is the local automated sweep, this time a 14B Qwen reading 645 files one at a time. It found nothing real. ComfyUI is hardened enough that a shallow per-file pass slides off it; its real weak points are cross-file and architectural, which a per-file reader does not see. Everything below came out of the slower layer that reads the actual source against the evidence ladder and tries to disprove each lead. That is the part I said last time was where the value is. The cloud bill for the run was about **1.8 cents**.

## What made the cut

Lucent surfaced the usual stack of leads. The discipline from last time still applies: read each one against the source, try to break it. But a target with a published `SECURITY.md`

adds a second ruler. Does the thing even count as a vulnerability under the project's *own* threat model? Measured that way, one finding made the cut.

| What | Where | Verdict |
|---|---|---|
Arbitrary file write outside `output/` |
dataset "Save" nodes, from a loaded workflow | In scope: filed as a GitHub advisory (Medium) |
| Path traversal → read an image from outside the models tree | `/experiment/models/preview` |
Real; image-bounded here, browser vector blocked by default. A later round found a verbatim read in this class (
|
`torch.load` with no `weights_only` on attacker bytes |
`LoadTrainingDataset` , via `/prompt` |
Real, but latent on a default `torch` (≥2.6 blocks it); RCE only on stale `torch<2.6` |
| Admin actions (reboot, snapshot/restore) reachable unauthenticated at the default | ComfyUI-Manager | Hardening: admin / denial-of-service, not RCE |
`install_model` fetches an unchecked URL for `.safetensors` names |
ComfyUI-Manager | Hardening: SSRF + model poisoning |
| Bearer tokens written in cleartext to a web-served log directory | API-node request logger | Low |
| Websocket client-id can be taken over → another client's outputs | `/ws` |
Low |

The one that counts is the file write. ComfyUI's experimental dataset "Save" nodes take a free-text `folder_name`

and join it straight onto the output directory: `os.path.join(get_output_directory(), folder_name)`

at `nodes_dataset.py:255`

, with no containment check. Then they `os.makedirs`

it and write. An absolute path or a `..`

chain walks out of `output/`

. The vector is the one ComfyUI's `SECURITY.md`

names as in scope: a workflow built from nothing but built-in nodes, the kind people share as JSON and bake into PNGs, writes a file wherever the author chose the moment you load and run it.

A two-node graph is the whole exploit:

```
curl -s -X POST http://127.0.0.1:8188/prompt -d '{"prompt":{
  "1":{"class_type":"EmptyImage","inputs":{"width":32,"height":32,"batch_size":1,"color":65280}},
  "2":{"class_type":"SaveImageDataSetToFolder",
       "inputs":{"images":["1",0],"folder_name":"/tmp/comfyui_live_escape_abs","filename_prefix":"pwn"}}}}'
# -> 200  {"prompt_id":"…","node_errors":{}}
```

I ran it against a fresh 0.22.0 (clean `git clone`

and `pip install`

, `--cpu`

, bound to loopback, no extra flags). The server accepted the graph and wrote the file outside its sandbox. Its own log is the receipt:

```
[INFO] Saved 1 images to /tmp/comfyui_live_escape_abs.
[INFO] Saved 1 images to …/comfyui-live/output/../../../../../tmp/comfyui_live_escape_rel.
```

Absolute paths and `..`

chains both land. The control is what makes it a bug and not a feature: a standard `SaveImage`

node handed the same traversal errors out and never creates the directory. It routes through `folder_paths.get_save_image_path()`

, which raises "outside the output folder." The dataset Save nodes just omit the guard their mainstream siblings have. The attacker picks the directory. The filename is fixed and the bytes are a PNG shaped with `EmptyImage`

, which is still enough to drop a file into an autostart or config path the server user can write.

It scores Medium. There is no remote, no-interaction version; it needs you to load and run the attacker's workflow on your own box. The fix is the guard the siblings already use, `+19/−4`

in one file. I filed it as a GitHub security advisory.

The image-read traversal is the same root-cause mistake: an experimental endpoint that forgot the containment its siblings (`/view`

, uploads, user data) all have. This endpoint is bounded. It leaks images plus a yes/no file-existence oracle, not arbitrary text, and the cross-origin browser path is blocked by ComfyUI's default middleware. So from this sweep I filed only the write and set the read aside. That call aged badly. The read-traversal class is real enough that a sibling `LoadImage`

version was already public, and a later round of grinding turned up a read in the same class that returns *any* file byte-for-byte. ComfyUI has since contained the shared helper both of them ran through, on 3 July 2026, and published the `LoadImage`

traversal as `GHSA-rvxv-29p8-pxgq`

on 15 July. That one is the strongest finding in the whole audit, filed and since fixed upstream. I wrote it up separately: [Reading Any File in ComfyUI](/reading-any-file-in-comfyui).

The pickle finding is the honest walk-back. `LoadTrainingDataset`

calls `torch.load`

without `weights_only`

, reachable through the unauthenticated `/prompt`

executor with no operator action. For a while it looked like a clean two-request remote-code-execution chain: upload a malicious pickle, then submit the graph that loads it. Then I checked what `pip install torch`

resolves to. It pulls the latest wheel, where `weights_only=True`

is the default and the payload is refused. So on a default install it is latent, not exploitable; a working exploit needs a stale or deliberately pinned `torch<2.6`

.

## The lead I killed

The one I most wanted to be real was the math node. ComfyUI's `MathExpression`

evaluates a workflow-supplied string through `simple_eval`

, and workflows travel constantly as JSON files and baked into PNGs from untrusted sources. That is untrusted input straight into an evaluator, no misconfiguration required. I ran all eight of the well-known `simpleeval`

sandbox escapes through the exact call ComfyUI makes. Every one was refused, both on the version a fresh install pulls and on a years-old release. It is a fragile design, resting on one general-purpose sandbox continuing to hold, but it is not a hole today, so it came off the list.

## What I filed, and what I'd send along

I filed one advisory. The file write is the single finding that crosses ComfyUI's own line, a workflow of built-in nodes writing outside the expected directories, so that is the one I reported, with a few-line fix.

The rest is a hardening list rather than advisories: the image-read traversal (bounded at this endpoint, though the class later produced a filed bug, linked above), the pickle path (needs a stale `torch`

), and the Manager's admin-and-SSRF gaps (real, but not code execution). The most useful non-code thing for the maintainers is documentation: say plainly that multi-user is not authentication, that the Manager's default security level allows administrative actions, and that ComfyUI assumes a trusted operator and should not be exposed to an untrusted network.

*Source was read but not modified, on Comfy-Org/ComfyUI and ComfyUI-Manager at their current main. Severities are my own estimates. The file-write finding is filed as a GitHub security advisory; the live findings were reproduced against a fresh 0.22.0 install on loopback.*

*Disclosure timeline: audit run and findings drafted 1 June 2026 against ComfyUI main (70a2e1a) and ComfyUI-Manager. The file-write finding was reported to Comfy Org directly. The stronger file-read finding from the later rounds was reported as a GitHub security advisory on 1 June 2026, and ComfyUI shipped path containment for the shared helper on 3 July 2026 (96e0e35), hardened 2 August (41a3e16). Published 11 August 2026. Reproduction details throughout are given against 0.22.0.*
