
Exploit para vulnerabilidade de injeção de comandos encontrada no binário uhttpd da câmera IP TP-Link Tapo c200
CVE-2021-4045 é uma vulnerabilidade de Injeção de Comandos que permite Execução Remota de Código na câmera IP TP-Link Tapo c200. Afeta todas as versões de firmware anteriores a 1.1.16 Build 211209 Rel. 37726N devido a verificações insuficientes na entrada do usuário em uhttpd, que é um dos principais binários do dispositivo. O artigo completo pode ser encontrado aqui.
Esta é uma versão estendida do PoC feita para demonstrar a vulnerabilidade na CryptoParty Madrid 2022. Possui 2 modos:
shell: Abre um shell root no alvo

rtsp: Fornece acesso ao fluxo de vídeo ao vivo via RTSP

A função exec_and_read_json usa popen para executar comandos:

exec_and_read_json é usada por 2 funções sem nome, que nomeei de set_language e wifi_connect. Elas lidam respectivamente com a configuração de idioma e wifi (obviamente). wifi_connect parece analisar aspas simples ('), porém set_language não. Isso significa que se pudermos controlar a entrada da função set_language, podemos injetar nossos próprios comandos com sucesso.

A função set_language é usada por uh_slp_proto_request, a função que mencionei antes, que passa como entrada alguns dados analisados recebidos do usuário.

Para analisar os dados do usuário, uh_slp_proto_request verifica se é um objeto JSON válido. Em seguida, obtém um valor de string identificado pela chave "method" e um valor de dicionário identificado por "params" (pelo menos é o que penso, já que a chamada de função não pôde ser resolvida pelo Ghidra, mas parecia funcionar dessa forma). Dependendo do método selecionado, uh_slp_proto_request seleciona a função que será chamada.
Então, ao enviar {"method": "setLanguage", "params":{}}, chamamos com sucesso a função set_language e passamos {} como parâmetro language_json. Em seguida, dentro de set_language, o objeto language_json é convertido em string e inserido diretamente em "ubus call system_state_audio set_language \'%s\'" para ser executado.
Ao enviar {"method": "setLanguage", "params": {"payload": "'; touch poc;'"}}, será executado ubus call system_state_audio set_language '{"payload": "'; touch poc;'"}', que na verdade contém 3 comandos: ubus call system_state_audio set_language '{"payload": "', touch poc, e '"}'. O segundo nos dá execução total de código.
Agora, uh_slp_proto_request é usada por outra função sem nome que gerencia todas as requisições, a qual nomeei de main_server_function. Se uma requisição for válida (não excede o comprimento máximo, usa http ou https dependendo da configuração do servidor, etc.), main_server_function verifica se a URL contém /cgi-bin/luci ou /web-static. Se não contiver, uh_slp_proto_request é chamada.

Ao adivinhar e enviar algumas requisições para a câmera, podemos verificar que os dados usados por uh_slp_proto_request são dados POST comuns. Então, se enviarmos uma requisição POST para / com o payload anterior, uh_slp_proto_request processará esses dados, chamará set_language e nosso payload será injetado no comando executado por exec_and_get_result.
Como pode ver, não mencionei nada sobre autenticação, já que o método setLanguage pode ser chamado sem login. Isso permite que qualquer usuário assuma o controle total da câmera com apenas uma requisição não autenticada.