Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
CVE-2021-4045 — Эксплойт внедрения команд для камеры TP-Link Tapo C200 (CVE-2021-4045), обеспечивающий доступ к root shell через UART и анализ обратно-инженерного бинарного файла uhttpd. | Kitploit
Инструменты/GitHubGitHub/kaleth4/cve-2021-4045
Безопасность встроенных системБезопасность IoTАнализ уязвимостейЭксплуатацияОбратная инженерияАппаратный ХакингТестирование на ПроникновениеКомандование и УправлениеАнализ Прошивок
GitHubkaleth4/cve-2021-4045

CVE-2021-4045

Эксплойт внедрения команд для камеры TP-Link Tapo C200 (CVE-2021-4045), обеспечивающий доступ к root shell через UART и анализ обратно-инженерного бинарного файла uhttpd.

2 месяцев назадЕщё не проверено

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться
Репозиторий

🔍 CVE-2021-4045: Уязвимость внедрения команд в TP-Link Tapo C200

image

CVE-2021-4045


📌 Резюме

CVE-2021-4045 — это уязвимость внедрения команд, обнаруженная в камере TP-Link Tapo C200, которая позволяет атакующему полностью захватить устройство с привилегиями root. Эта уязвимость затрагивает все версии прошивки до 1.1.16 Build 211209 Rel. 37726N.

🔗 Официальное уведомление INCIBE: https://www.incibe.es/incibe-cert/alerta-temprana/vulnerabilidades/cve-2021-4045 (Заменить на реальную ссылку)

🔧 Рекомендуемое решение: Обновить прошивку до версии 1.1.16 или выше.



🛠 Первоначальная разведка

Конфигурация и характеристики устройства

  • Недорогая IP-камера (~30€) с расширенными функциями:
    • Запись на SD-карту.
    • Горизонтальный поворот на 360° и вертикальный на 90°.
    • Воспроизведение аудио в реальном времени из мобильного приложения.

Сканирование портов```bash

$ nmap -sV -p- 192.168.1.81

root@kitploit:~
**Результат**:```
PORT     STATE SERVICE
443/tcp  open  https
554/tcp  open  rtsp
2020/tcp open  xinupageserver
8800/tcp open  sunwebadmin
image Как видите, на устройстве имеется несколько интересных открытых портов. Первым делом я проверил порт 443. Хотя nmap четко указывает, что используется https, при начальном сканировании я это упустил и потратил довольно много времени, думая, что порт 443 использует http. Из-за этого я проверял только http://192.168.1.81:443 вместо https://192.168.1.81:443, поэтому получал только ответы 400. Как я уже говорил во введении, этот процесс был полон ошибок. Что касается остальных портов, то службы, работающие на них, были мне совершенно незнакомы, и я не нашел никакой четкой информации о них. В тот момент у меня закончились известные варианты, и пришло время копать глубже.

----[ Получение shell ]-------------------------------

Перед покупкой камеры я искал в интернете предыдущие исследования этого устройства и, к счастью, нашел этот репозиторий на GitHub, где люди совместно проводили обратную разработку. В одной из проблем объяснялось, как получить доступ к консоли через порт UART, о чем я тогда совершенно не знал. Поэтому я изучил основы и купил преобразователь USB в TTL для подключения. image С помощью упомянутой проблемы мне удалось вскрыть устройство ножом и отверткой и быстро найти UART. После нескольких попыток и большого терпения я наконец припаял несколько проводов к контактным площадкам.

image

Затем настало время проверить, достаточно ли хороша пайка для передачи данных. Я подключил провода к USB-адаптеру, учитывая, что Rx UART идет к Tx адаптера и наоборот, и подключил адаптер к компьютеру. Опять же, благодаря упомянутой проблеме, я знал, что скорость передачи для последовательного соединения равна 57600, поэтому я выполнил:

$ sudo screen /dev/tty.usbserial-0001 57600

Где '/dev/tty.usbserial-0001' — это USB-порт, к которому подключен адаптер и который питает устройство. Я сразу же начал получать данные, отлично.

Однако доступа к консоли у меня еще не было. Я получал просто последовательность загрузки устройства, которая на самом деле была загрузчиком U-Boot. Выглядело это примерно так:

U-Boot 2014.01-v1.2 (Jul 16 2021 - 18:41:10)

Board: IPCAM RTS3903 CPU: 500M :rx5281 prid=0xdc02 force spi nor mode DRAM: 64 MiB @ 1066 MHz Skipping flash_init Flash: 0 Bytes flash status is 0, 0, 0 SF: Detected XM25QH64A with page size 256 Bytes, erase size 64 KiB, total 8 MiB Using default environment

Autobooting in 1 seconds copying flash to 0x81500000 flash status is 0, 0, 0 SF: Detected XM25QH64A with page size 256 Bytes, erase size 64 KiB, total 8 MiB SF: 8388608 bytes @ 0x0 Read: OK

[...]

При нажатии Enter нас просят ввести имя пользователя и пароль. Благодаря той проблеме на GitHub мы знаем учетные данные, поэтому можем успешно войти с пользователем 'root' и паролем 'slprealtek' и, наконец, получить доступ к консоли.

Как только я убедился, что соединение работает, мне нужно было укрепить пайку, так как она дважды ломалась в процессе сборки корпуса. Я нанес термоклей, чтобы закрепить все провода, и закрыл устройство, отключив все двигатели. Теперь мой тестовый блок был готов.

image

----[ Изучение устройства ]--------------------------

Теперь, когда у нас есть корпус, давайте изучим устройство:

root@SLP:~# uname -a Linux SLP 3.10.27 #1 PREEMPT Wed Nov 11 20:42:05 CST 2020 rlx GNU/Linux

root@SLP:~# cat /etc/openwrt_version 12.09-rc1

Как видим, это машина на OpenWRT, работающая под управлением Linux 3.10.27. Теперь проверим активные процессы и открытые порты:

root@SLP:~# ps PID USER VSZ STAT COMMAND 1 root 2328 S init 2 root 0 SW [kthreadd] 3 root 0 SW [ksoftirqd/0] 4 root 0 SW [kworker/0:0] 5 root 0 SW< [kworker/0:0H] 6 root 0 SW [kworker/u2:0] 7 root 0 SW [rcu_preempt] 8 root 0 SW [rcu_bh] 9 root 0 SW [rcu_sched] 10 root 0 SW< [khelper] 11 root 0 SW< [writeback] 12 root 0 SW< [bioset] 13 root 0 SW< [kblockd] 14 root 0 SW [khubd] 15 root 0 SW [kworker/0:1] 16 root 0 SW [kswapd0] 17 root 0 SW [fsnotify_mark] 18 root 0 SW< [crypto] 27 root 0 SW [kworker/u2:1] 46 root 0 SW< [deferwq] 47 root 0 SW< [kworker/0:1H] 247 root 2328 S -ash 262 root 0 SW [irq/27-gpio res] 273 root 0 SW< [cryptodev_queue] 282 root 860 S /sbin/hotplug2 --override --persistent --set-rules-f 304 root 888 S /sbin/ubusd 325 root 8152 S tp_manage 357 root 3416 S /usr/bin/ledd 361 root 3408 S /sbin/msglogd 367 root 3220 S /usr/sbin/netlinkd 370 root 5468 S < /usr/bin/system_state_audio 379 root 10180 S /usr/sbin/wlan-manager 491 root 1636 S /sbin/netifd 492 root 1520 S /usr/sbin/connModed 494 root 11488 S /usr/bin/dsd 496 root 1532 S /usr/sbin/connModed 502 root 7640 S /bin/cloud-service 520 root 4360 S /bin/cloud-brd -c /var/etc/cloud_brd_conf 653 root 15020 S /bin/cloud-client 830 root 2320 S /usr/sbin/telnetd -b 127.0.0.1 861 root 3852 S /usr/sbin/uhttpd -f -h /www -T 180 -A 0 -n 8 -R -r C 870 root 6048 S /usr/bin/relayd 872 root 5948 S /usr/bin/rtspd 879 root 4612 S /usr/bin/p2pd 884 root 11152 S /bin/dn_switch 889 root 4180 S /bin/storage_manager 920 root 40940 S /bin/cet 956 root 32336 S /bin/vda 960 root 3808 S /bin/wtd 970 root 11288 S /bin/nvid 1019 root 2332 S udhcpc -p /var/run/static-dhcpc.pid -s /lib/netifd/s 1037 root 0 SW [RTW_CMD_THREAD] 1059 root 1212 S wpa_supplicant -B -Dwext -iwlan0 -P/tmp/supplicant_p 1089 root 2332 S /usr/sbin/ntpd -n -p time.nist.gov -p 133.100.9.2 -p 1103 root 3840 S /usr/bin/motord 1447 root 2324 R ps

root@SLP:~# netstat -natpu Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:8800 0.0.0.0:* LISTEN 920/cet tcp 0 0 127.0.0.1:929 0.0.0.0:* LISTEN 875/p2pd tcp 0 0 0.0.0.0:20002 0.0.0.0:* LISTEN 325/tp_manage tcp 0 0 0.0.0.0:2020 0.0.0.0:* LISTEN 969/nvid tcp 0 0 0.0.0.0:554 0.0.0.0:* LISTEN 920/cet tcp 0 0 127.0.0.1:23 0.0.0.0:* LISTEN 832/telnetd tcp 0 0 127.0.0.1:921 0.0.0.0:* LISTEN 878/relayd tcp 0 0 127.0.0.1:922 0.0.0.0:* LISTEN 877/rtspd tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN 863/uhttpd tcp 0 0 192.168.1.80:37380 52.19.66.90:443 ESTABLISHED 507/cloud-brd udp 0 0 0.0.0.0:20002 0.0.0.0:* 325/tp_manage udp 0 0 0.0.0.0:38000 0.0.0.0:* 1087/ntpd udp 0 0 0.0.0.0:3702 0.0.0.0:* 969/nvid

Мы видим процессы, стоящие за теми открытыми портами, которые были обнаружены при сканировании nmap, например uhttpd или cet. Я сосредоточился конкретно на процессе uhttpd, так как он стоит за https-сервером (который в то время я все еще считал http) и уже был хорошо знаком с протоколами http.

uhttpd — это веб-сервер, созданный OpenWRT для использования на встроенных устройствах, работающих под управлением этого дистрибутива. На этом этапе я хотел узнать, можно ли получить о нем больше информации, например исходный код или хотя бы пути. Я посетил wiki OpenWRT и узнал о uhttpd и OpenWRT в целом. На машинах OpenWRT существует система, называемая Unified Configuration Interface (UCI), которая в основном используется для простой настройки системных служб. Используя это, мы можем получить конфигурацию uhttpd:

root@SLP:~# uci show | grep uhttpd ucitrack.@uhttpd[0]=uhttpd ucitrack.@uhttpd[0].init=uhttpd uhttpd.main=uhttpd uhttpd.main.listen_https=443 uhttpd.main.home=/ww uhttpd.main.rfc1918_filter=1 uhttpd.main.max_requests=8 uhttpd.main.cert=/tmp/uhttpd.crt uhttpd.main.key=/tmp/uhttpd.key uhttpd.main.cgi_prefix=/cgi-bin uhttpd.main.lua_prefix=/luci uhttpd.main.lua_handler=/usr/lib/lua/luci/sgi/uhttpd.lua uhttpd.main.script_timeout=180 uhttpd.main.network_timeout=180 uhttpd.main.tcp_keepalive=0 uhttpd.px5g=cert uhttpd.px5g.days=3600 uhttpd.px5g.bits=1024 uhttpd.px5g.country=CN uhttpd.px5g.state=China uhttpd.px5g.location=China uhttpd.px5g.commonname=TP-Link upnpc.uhttpd=entry upnpc.uhttpd.proto=TCP upnpc.uhttpd.ext_port=80 upnpc.uhttpd.desc=uhttpd

Здесь есть несколько интересных параметров. Во-первых, 'uhttpd.main.home' указывает на корневой каталог сервера, поэтому мы могли бы найти там некоторые файлы веб-сервера. Затем, 'uhttpd.main.lua_handler' указывает на скрипт обработчика Lua, который используется для инициализации среды выполнения Lua при запуске сервера, так как uhttpd поддерживает скрипты Lua, так что там могло быть больше интересных файлов. Однако каталог '/www' пуст, а в '/usr/lib/lua/luci' нет каталога 'sgi' и файла 'uhttpd.lua' в системе. Я попытался найти информацию о том, как работает этот экземпляр uhttpd, но ничего не нашел, только параметры конфигурации, которые никуда не ведут.

На этом этапе я понял, что решение заключалось в том, чтобы напрямую проанализировать бинарник 'uhttpd' и применить обратную разработку, но перед этим я хотел создать тестовое окружение, чтобы узнать, что происходит внутри веб-сервера при выполнении запросов, поскольку, судя по тому, как был создан процесс, никакого вывода нигде не было.

Я попытался выполнить команду, найденную в выводе команды ps для процесса 861:

$ /usr/sbin/uhttpd -f -h /www -T 180 -A 0 -n 8 -R -r C

Однако я получил много ошибок и не смог заставить его работать. Поскольку мне не удалось создать тот же процесс uhttpd на другом порту, я попытался найти недостающий вывод, проверив запись '/proc' процесса, чтобы попытаться прочитать их, если они существуют (как объясняется в этом видео PwnFunction). Но была одна большая проблема:

root@SLP:~# sudo ls -l /proc/864/fd/ lrwx------ 1 root root 64 Nov 10 22:44 0 -> /dev/null lrwx------ 1 root root 64 Nov 10 22:44 1 -> /dev/null lrwx------ 1 root root 64 Nov 10 22:44 2 -> /dev/null lrwx------ 1 root root 64 Nov 10 22:44 3 -> anon_inode:[eventpoll] lrwx------ 1 root root 64 Nov 10 22:44 4 -> socket:[1830]

Все файловые дескрипторы для 'stdin', 'stdout' и 'stderr' были перенаправлены в '/dev/null', что в основном перенаправляет их в черную дыру, где их невозможно найти. Я застрял и не знал, что делать. Поскольку я уже был в записи '/proc', я начал исследовать, так как не помнил, чтобы записи '/proc' содержали так много информации о процессе, и мне было любопытно. Благодаря этому случайному любопытству я наткнулся на запись 'environ', которая содержит все переменные окружения для этого процесса. Одной из этих переменных окружения была:

UHTTPD_ARGS=-h /www -T 180 -A 0 -n 8 -R -r C200 -C /tmp/uhttpd.crt -K /tmp/uhttpd.key -s 443

Я сразу понял, что команда, показанная ps, была неверной, и позже обнаружил, что это произошло из-за того, что интерфейс UART не имел достаточной ширины для отображения всех символов. Еще одна ошибка, которая преподала мне важные уроки: никогда не доверяйте выводу порта UART.

Теперь я наконец смог создать другой экземпляр uhttpd с теми же параметрами и без каналов в '/dev/null', чтобы протестировать бинарник, пока занимался его обратной разработкой.

----[ Обратная разработка uhttpd с помощью Ghidra ]--------

Это был мой первый раз, когда я использовал Ghidra. Я видел несколько видео и читал несколько статей о ней (спасибо stacksmashing и liveoverflow за потрясающий и легкоусвояемый контент), но никогда на самом деле не использовал ее, так что это была очень хорошая возможность научиться.

Я открыл бинарник uhttpd и после нескольких попыток обнаружил, что язык — MIPS32, little endian, с mips16e. Некоторые имена функций поставлялись по умолчанию с бинарником, но другие — нет. Я также потратил некоторое время на переименование функций, так как, по-видимому, Ghidra часто путается с внешними функциями и получаются странные обертки для них, например:

image Я проанализировал функцию `main()` и другие важные, чтобы понять логику бинарника и его структуру. Я нашел несколько интересных, уже идентифицированных, среди которых были `do_login()` и `uh_slp_proto_request()`. Я расскажу подробнее о последней позже.

После этого первого знакомства я начал искать ошибки. Поскольку я полный новичок в уязвимостях переполнения, первым делом я поискал вызовы system(), exec() и popen(), чтобы проверить, не существует ли какой-нибудь уязвимости внедрения команд, которую я мог бы легко использовать. И мне очень повезло.

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

ejecutar_y_leer_json

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

conexión wifi establecer_idioma

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

función_principal_1 función_principal_2

Чтобы проанализировать данные пользователя, 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;'"}'

На самом деле это содержит 3 команды:

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_entrypoint

При тестировании и отправке пары запросов на камеру, мы можем убедиться, что данные, используемые 'uh_slp_proto_request()', являются стандартными POST-данными. Следовательно, если мы отправим POST-запрос на '/' с вышеуказанной полезной нагрузкой, 'uh_slp_proto_request()' обработает эти данные, вызовет 'set_language()', и наша нагрузка будет внедрена в команду, выполняемую 'exec_and_get_result()'.

Как видите, я не упоминал об аутентификации, поскольку функцию 'setLanguage()' можно вызвать без входа в систему. Это позволяет любому пользователю полностью захватить контроль над камерой одним запросом без аутентификации.

----[ Эксплуатация ]----------------------------------

Теперь пришло время написать эксплоит. Я потратил некоторое время, чтобы выяснить, как получить обратную оболочку с помощью netcat. Казалось, это просто, но у меня не получалось. Я обнаружил, что версия netcat, установленная в BusyBox, сильно ограничена по функциональности, поэтому обычные обратные оболочки не подходили. Однако я нашел то, что искал, в репозитории PayloadsAllTheThings (как всегда) и получил идеальную обратную оболочку. Поскольку uhttpd работает от root (спасибо TP-Link), мы получаем оболочку с максимальными привилегиями, просто отправив вредоносный POST-запрос. Эксплоит доступен на странице GitHub: https://github.com/hacefresko/CVE-2021-4045/blob/master/pwntapo.py image

🚨 Урок усвоен:

  • Я спутал https с http на порту 443, теряя время, пока не обнаружил ошибку.
  • Службы на портах 2020, 554 и 8800 были неизвестны, что потребовало дополнительного исследования.

🔓 Получение оболочки

Доступ к консоли через UART

  1. Предварительное исследование:
    • Я нашел репозиторий GitHub с информацией об обратной разработке устройства.
    • Я научился использовать преобразователь USB в TTL для доступа к порту UART.
Скачать инструмент