
Эксплойт для уязвимости внедрения команд в бинарный файл uhttpd из IP-камеры TP-Link Tapo c200
CVE-2021-4045 — это уязвимость внедрения команд, позволяющая удаленно выполнять код в IP-камере TP-Link Tapo c200. Она затрагивает все версии прошивки до 1.1.16 Build 211209 Rel. 37726N из-за недостаточной проверки пользовательского ввода в uhttpd, одном из основных бинарных файлов устройства. Полное описание можно найти здесь.
Это расширенная версия PoC, созданная для демонстрации уязвимости на CryptoParty Madrid 2022. Имеет 2 режима:
shell: Запускает root-оболочку на целевом устройстве

rtsp: Предоставляет доступ к живому видеопотоку через RTSP

Функция exec_and_read_json использует popen для выполнения команд:

exec_and_read_json используется двумя безымянными функциями, которые я назвал set_language и wifi_connect. Они отвечают за настройку языка и Wi-Fi соответственно (очевидно). wifi_connect, похоже, экранирует одинарные кавычки ('), однако set_language этого не делает. Это означает, что если мы можем контролировать ввод для функции set_language, мы можем успешно внедрить собственные команды.

Функция set_language используется функцией uh_slp_proto_request, о которой я упоминал ранее. Она передает на вход некоторые разобранные данные, полученные от пользователя.

Для разбора пользовательских данных uh_slp_proto_request проверяет, является ли строка валидным JSON-объектом. Затем она извлекает строковое значение по ключу "method" и словарь по ключу "params" (по крайней мере, я так думаю, поскольку вызов функции не удалось разрешить в Ghidra, но это, похоже, работает). В зависимости от выбранного метода uh_slp_proto_request выбирает вызываемую функцию.
Таким образом, отправив {"method": "setLanguage", "params":{}}, мы успешно вызываем функцию set_language и передаем {} в качестве параметра language_json. Затем внутри set_language объект language_json преобразуется в строку и подставляется непосредственно в "ubus call system_state_audio set_language \'%s\'" для выполнения.
Отправив {"method": "setLanguage", "params": {"payload": "'; touch poc;'"}}, будет выполнена команда ubus call system_state_audio set_language '{"payload": "'; touch poc;'"}', которая фактически содержит три команды: ubus call system_state_audio set_language '{"payload": "', touch poc и '"}'. Вторая команда дает нам полное выполнение кода.
Теперь uh_slp_proto_request используется другой безымянной функцией, управляющей всеми запросами, которую я назвал main_server_function. Если запрос корректен (не превышает максимальную длину, использует http или https в зависимости от конфигурации сервера и т.д.), main_server_function проверяет, содержит ли URL /cgi-bin/luci или /web-static. Если нет, то вызывается uh_slp_proto_request.

Путем подбора и отправки нескольких запросов камере можно убедиться, что данные, используемые uh_slp_proto_request, являются обычными POST-данными. Таким образом, если отправить POST-запрос на / с предыдущей полезной нагрузкой, uh_slp_proto_request обработает эти данные, вызовет set_language, и наша полезная нагрузка будет внедрена в команду, выполняемую exec_and_get_result.
Как видите, я не упомянул аутентификацию, поскольку метод setLanguage можно вызвать без входа в систему. Это позволяет любому пользователю получить полный контроль над камерой всего одним неаутентифицированным запросом.