{"slug": "one-bug-made-the-cut-lucent-on-comfyui", "title": "One Bug Made the Cut: Lucent on ComfyUI", "summary": "An audit of ComfyUI by a former Stability AI CISO found one vulnerability that made the cut: an arbitrary file write outside the output directory via dataset \"Save\" nodes, filed as a Medium-severity GitHub advisory. The audit, which cost about 1.8 cents in cloud compute, concluded ComfyUI is well-built under its single-trusted-operator threat model but noted that multi-user mode is not authentication and the Manager's security levels allow more at default than the name suggests.", "body_md": "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\n\n[. 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)\n\n**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.\n\nAnd 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.\n\nThe 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.\n\nA 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**.\n\n## What made the cut\n\nLucent 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`\n\nadds 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.\n\n| What | Where | Verdict |\n|---|---|---|\nArbitrary file write outside `output/` |\ndataset \"Save\" nodes, from a loaded workflow | In scope: filed as a GitHub advisory (Medium) |\n| Path traversal → read an image from outside the models tree | `/experiment/models/preview` |\nReal; image-bounded here, browser vector blocked by default. A later round found a verbatim read in this class (\n|\n`torch.load` with no `weights_only` on attacker bytes |\n`LoadTrainingDataset` , via `/prompt` |\nReal, but latent on a default `torch` (≥2.6 blocks it); RCE only on stale `torch<2.6` |\n| Admin actions (reboot, snapshot/restore) reachable unauthenticated at the default | ComfyUI-Manager | Hardening: admin / denial-of-service, not RCE |\n`install_model` fetches an unchecked URL for `.safetensors` names |\nComfyUI-Manager | Hardening: SSRF + model poisoning |\n| Bearer tokens written in cleartext to a web-served log directory | API-node request logger | Low |\n| Websocket client-id can be taken over → another client's outputs | `/ws` |\nLow |\n\nThe one that counts is the file write. ComfyUI's experimental dataset \"Save\" nodes take a free-text `folder_name`\n\nand join it straight onto the output directory: `os.path.join(get_output_directory(), folder_name)`\n\nat `nodes_dataset.py:255`\n\n, with no containment check. Then they `os.makedirs`\n\nit and write. An absolute path or a `..`\n\nchain walks out of `output/`\n\n. The vector is the one ComfyUI's `SECURITY.md`\n\nnames 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.\n\nA two-node graph is the whole exploit:\n\n```\ncurl -s -X POST http://127.0.0.1:8188/prompt -d '{\"prompt\":{\n  \"1\":{\"class_type\":\"EmptyImage\",\"inputs\":{\"width\":32,\"height\":32,\"batch_size\":1,\"color\":65280}},\n  \"2\":{\"class_type\":\"SaveImageDataSetToFolder\",\n       \"inputs\":{\"images\":[\"1\",0],\"folder_name\":\"/tmp/comfyui_live_escape_abs\",\"filename_prefix\":\"pwn\"}}}}'\n# -> 200  {\"prompt_id\":\"…\",\"node_errors\":{}}\n```\n\nI ran it against a fresh 0.22.0 (clean `git clone`\n\nand `pip install`\n\n, `--cpu`\n\n, bound to loopback, no extra flags). The server accepted the graph and wrote the file outside its sandbox. Its own log is the receipt:\n\n```\n[INFO] Saved 1 images to /tmp/comfyui_live_escape_abs.\n[INFO] Saved 1 images to …/comfyui-live/output/../../../../../tmp/comfyui_live_escape_rel.\n```\n\nAbsolute paths and `..`\n\nchains both land. The control is what makes it a bug and not a feature: a standard `SaveImage`\n\nnode handed the same traversal errors out and never creates the directory. It routes through `folder_paths.get_save_image_path()`\n\n, 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`\n\n, which is still enough to drop a file into an autostart or config path the server user can write.\n\nIt 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`\n\nin one file. I filed it as a GitHub security advisory.\n\nThe image-read traversal is the same root-cause mistake: an experimental endpoint that forgot the containment its siblings (`/view`\n\n, 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`\n\nversion 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`\n\ntraversal as `GHSA-rvxv-29p8-pxgq`\n\non 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).\n\nThe pickle finding is the honest walk-back. `LoadTrainingDataset`\n\ncalls `torch.load`\n\nwithout `weights_only`\n\n, reachable through the unauthenticated `/prompt`\n\nexecutor 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`\n\nresolves to. It pulls the latest wheel, where `weights_only=True`\n\nis 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`\n\n.\n\n## The lead I killed\n\nThe one I most wanted to be real was the math node. ComfyUI's `MathExpression`\n\nevaluates a workflow-supplied string through `simple_eval`\n\n, 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`\n\nsandbox 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.\n\n## What I filed, and what I'd send along\n\nI 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.\n\nThe 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`\n\n), 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.\n\n*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.*\n\n*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.*", "url": "https://wpnews.pro/news/one-bug-made-the-cut-lucent-on-comfyui", "canonical_source": "https://blog.attacks.ai/lucent-on-comfyui", "published_at": "2026-08-11 00:00:00+00:00", "updated_at": "2026-08-11 01:08:03.843565+00:00", "lang": "en", "topics": ["ai-tools", "ai-safety", "ai-policy"], "entities": ["ComfyUI", "Comfy Org", "Stability AI", "ComfyUI-Manager", "Qwen", "Lucent"], "alternates": {"html": "https://wpnews.pro/news/one-bug-made-the-cut-lucent-on-comfyui", "markdown": "https://wpnews.pro/news/one-bug-made-the-cut-lucent-on-comfyui.md", "text": "https://wpnews.pro/news/one-bug-made-the-cut-lucent-on-comfyui.txt", "jsonld": "https://wpnews.pro/news/one-bug-made-the-cut-lucent-on-comfyui.jsonld"}}