
Exploit per una vulnerabilità di iniezione di comandi trovata nel binario uhttpd della telecamera IP TP-Link Tapo c200
CVE-2021-4045 è una vulnerabilità di Command Injection che consente l'esecuzione remota di codice nella telecamera IP TP-Link Tapo c200. Colpisce tutte le versioni del firmware precedenti alla 1.1.16 Build 211209 Rel. 37726N a causa di controlli insufficienti sull'input utente in uhttpd, che è uno dei binari principali del dispositivo. La descrizione completa si trova qui.
Questa è una versione estesa del PoC realizzato per mostrare la vulnerabilità al CryptoParty Madrid 2022. Ha 2 modalità:
shell: Avvia una shell di root sul target

rtsp: Fornisce accesso al flusso video in diretta tramite RTSP

La funzione exec_and_read_json usa popen per eseguire comandi:

exec_and_read_json è usata da 2 funzioni senza nome, che ho chiamato set_language e wifi_connect. Esse gestiscono rispettivamente la configurazione della lingua e del wifi (ovviamente). wifi_connect sembra analizzare gli apici singoli ('), mentre set_language no. Ciò significa che se possiamo controllare l'input per la funzione set_language, possiamo iniettare con successo i nostri comandi.

La funzione set_language è usata da uh_slp_proto_request, la funzione che ho menzionato prima, la quale passa come input alcuni dati analizzati ricevuti dall'utente.

Per analizzare i dati dell'utente, uh_slp_proto_request controlla se si tratta di un oggetto JSON valido. Quindi, ottiene un valore stringa identificato dalla chiave "method" e un valore dizionario identificato da "params" (almeno questo è ciò che penso, poiché la chiamata di funzione non poteva essere risolta da Ghidra ma sembrava funzionare in questo modo). A seconda del metodo selezionato, uh_slp_proto_request seleziona la funzione che verrà chiamata.
Quindi, inviando {"method": "setLanguage", "params":{}} chiamiamo con successo la funzione set_language e passiamo {} come parametro language_json. Poi, all'interno di set_language, l'oggetto language_json viene convertito in stringa e inserito direttamente in "ubus call system_state_audio set_language \'%s\'" per essere eseguito.
Inviando {"method": "setLanguage", "params": {"payload": "'; touch poc;'"}}, verrà eseguito ubus call system_state_audio set_language '{"payload": "'; touch poc;'"}', che in realtà contiene 3 comandi: ubus call system_state_audio set_language '{"payload": "', touch poc e '"}'. Il secondo ci dà la piena esecuzione del codice.
Ora, uh_slp_proto_request è usata da un'altra funzione senza nome che gestisce tutte le richieste, che ho chiamato main_server_function. Se una richiesta è valida (non supera la lunghezza massima, usa http o https a seconda della configurazione del server, ecc.), main_server_function controlla se l'URL contiene /cgi-bin/luci o /web-static. In caso contrario, viene chiamata uh_slp_proto_request.

Indovinando e inviando un paio di richieste alla telecamera, possiamo verificare che i dati utilizzati da uh_slp_proto_request siano normali dati POST. Quindi, se inviamo una richiesta POST a / con il payload precedente, uh_slp_proto_request elaborerà questi dati, chiamerà set_language e il nostro payload sarà iniettato nel comando eseguito da exec_and_get_result.
Come potete vedere, non ho menzionato nulla riguardo all'autenticazione, poiché il metodo setLanguage può essere chiamato senza login. Ciò consente a qualsiasi utente di prendere il pieno controllo della telecamera con una sola richiesta non autenticata.