Содержание статьи

INFO
Этот текст основан на моем докладе Oldschool Way of Hacking Microdigital IP-cameras, с которым я выступил на ZeroNights 2019. Многие просили меня прислать слайды, и я решил, что статья с разбором всех уязвимостей будет еще полезнее.
Итак, моей задачей было выбрать такого производителя, который, с одной стороны, давно присутствует на российском рынке, с другой — еще не привлекал внимание специалистов по безопасности. Мой выбор пал на корейскую фирму Microdigital, которая производит IP-камеры.
Сайт компании обещает нам широкий ассортимент: «свыше 30 моделей регистраторов, свыше 150 моделей видеокамер». Отлично!

Компания существует на рынке (в том числе и российском) уже больше двенадцати лет, а это значит, что ее продукция распространена. Оказалось даже, что в 2011 году был заключен договор на оснащение более 30 тысяч российских автобусов камерами этой фирмы.
В первую очередь меня заинтересовали устройства серии N, они достаточно продвинутые, но при этом пока что не стали объектом тестирования кого-то из исследователей. Пора исправить это! Я выбрал модель MDC-N4090W, которая предназначена для использования внутри помещений. Подробную информацию об устройстве можно почерпнуть на сайте производителя.

Изучение камеры
Начинать любое исследование железа лучше всего с изучения доступной документации.

Открываем PDF, полученный на сайте Microdigital, и узнаем, что у камеры есть веб-интерфейс с пользователями root (пароль root) и anonymous.

Ну и раз уж мы на сайте компании, прихватим актуальную прошивку для камеры. Долго искать не пришлось, она доступна в соответствующем разделе.

Не факт, правда, что в прошивке содержится вся нужная для тестирования информация, поэтому смысл ее изучать будет, только если нет полноценного админского доступа в консоль устройства либо когда надо изучить апдейт. Поэтому не будем сейчас тратить время и вернемся к прошивке позже.
Подготовка устройства к тестированию
Приступим к изучению аппаратной составляющей. Для этого разбираем устройство (ничего сложного, четыре винта по периметру) и получаем печатную плату.


Также видим следующее:
- память S34ML01G100TF100;
- чип DM368ZCE;
- интерфейсы: четыре пина UART, USB, MicroSD, Ethernet.
Пины, отмеченные как BLE, я не рассматриваю, так как это, скорее всего, контакты для подключения модуля Bluetooth. Нас это в данный момент не интересует.
Модуль S34ML01G100TF100 — энергонезависимая NAND-память в корпусе TSOP-48. Datasheet легко гуглится. Из него узнаем подробнее о типе корпуса (NAND08) и размере хранилища — 128 Мбайт.
Для дальнейшей работы потребуется сделать бэкап данных, чтобы в случае «окирпичивания» камеры можно было вернуть ее в изначальное состояние. Для этого идеально подходит программатор ProMan TL86 или TL866 с переходником NAND08 → DIP48.
Содержимое флеш-памяти сохраним в нашу рабочую директорию. Как и к прошивке, возвращаться к ней нужно будет только в том случае, если не выйдет дорваться до админской консоли.

Для чипа DM368ZCE тоже не составило проблем нагуглить документацию (PDF). Оказывается, архитектура чипа — ARM. К тому же из документации можно достать его распиновку, но нам она не потребуется.

Пройдемся по интерфейсам. Из документации очевидно, что USB и MicroSD нужны в основном для того, чтобы подключать к устройству внешние носители и использовать их в качестве хранилища. Для полноты картины можем подключить к устройству USB-фаззер facedancer21 и, используя утилиту umap2scan, получить список поддерживаемых устройств.


К сожалению, камера не поддерживает ни одно из известных нам устройств.
Как насчет UART? Тут предстоит определить, за что отвечает каждый пин и какова скорость передачи данных. Для этого воспользуемся логическим анализатором Saleae Logic. Для удобства я подключился через проводок, который соединяет плату устройства и инфракрасные лампочки.

Пронумеруем пины для удобства.

Прежде чем включать логический анализатор, подключаем заземление к пину GND интерфейса для подключения BLE.

Теперь включаем логический анализатор и само устройство и смотрим, что из этого выйдет.

После включения устройства на пине номер 3 (в программе отсчет идет с нуля и пин нумерован как 2) передаются бинарные данные. Этот пин интерфейса UART отвечает за передачу данных (TX). Просмотрев длину одного бита, получаем текущую скорость передачи — 115 200 бит/с. При корректных настройках мы даже можем разглядеть часть текста.

У пина под номером 1 постоянное напряжение 3 В — следовательно, он предназначен для питания. Пин номер 4 связан с пином GND интерфейса для подключения модуля BLE. Значит, этот пин тоже «земля». И остается последний пин под номером 2, он отвечает за прием байтов (RX). Теперь у нас есть вся информация для общения с камерой по UART. Для подключения я воспользуюсь Arduino UNO в режиме переходника TTL.

Начинаем мониторить порт UART и получаем следующее.

При старте устройства первым делом подгружается загрузчик системы U-Boot. К сожалению, на уровне загрузки пин TX отключен в настройках камеры, поэтому мы можем наблюдать только отладочный вывод. Через какое-то время подгружается основная система, позволяющая ввести логин и пароль для доступа в администраторскую консоль. Пара root/root (аналогичная той, что используется для веб-админки и указана в документации) прекрасно подошла.

Получив консоль, мы можем изучить все работающие сервисы. Но не забываем, что у нас есть еще один неизученный интерфейс — Ethernet. Для его исследования нужно будет подготовить систему мониторинга трафика. Причем важно отслеживать первое же подключение к сети.

INFO
Мы должны начать перехватывать трафик сразу, поскольку некоторые устройства при первом подключении начинают скачивать обновления. Не факт, что в следующие разы получится перехватить коммуникации.
Для перехвата трафика я буду пользоваться устройством Lan Tap Pro.

Никакой связанной с обновлениями активности мы, впрочем, не обнаруживаем. На этом разведка закончена, и мы полностью готовы к поиску уязвимостей!
Сетевая часть
Просканируем порты утилитой Nmap и получим список открытых портов.

Пройдемся вкратце по доступным нам сервисам.
FTP
При подключении сервис запрашивает логин и пароль. Анонимный вход отключен. Но и тут подошел вариант root/root!

Теперь мы можем заходить в любую директорию и получили удобный способ закидывать файлы на удаленный хост.
Telnet
При подключении по Telnet опять же требуется логин и пароль одного из реальных аккаунтов и уже не в первый раз подходит пара root/root. Обрати внимание, что нам теперь не нужна консоль UART, так как все то же самое можно делать удаленно по Telnet.

RTSP
Для подключения к RTSP опять же нужно авторизоваться как root/root. Ссылка для подключения принимает вид rtsp://root:root@192.168.1.151:554/Primary
.

Веб
Изучив устройство веб-сервера камеры, я составил вот такую схему.

На сервере находятся скрипты на PHP и CGI-приложения, которые общаются с исполняемыми файлами из директории /usr/local/ipsca/
(преимущественно общение идет с MainProc
). Для хранения всех настроек используется база данных SQLite 3.
С нее-то мы и начнем искать уязвимости. База данных хранится в /usr/local/ipsca/mipsca.db
. В ней лежит все — от логов системы до настроек автоматической загрузки записей камеры на удаленный сервер. Структура базы данных видна на скрине ниже.

Мое внимание привлекла таблица User. Она отвечает за работу с данными пользователей: логин, пароль, привилегии.

Пароль пользователя хранится в колонке Password в незашифрованном виде, то есть, получив доступ к базе данных, злоумышленник может узнать пароль администратора и протестировать его на других доступных сервисах.
Переходим к скриптам на PHP. В веб-директории /root/httpd/htdocs/Web
лежит три скрипта: download.php
, login.php
, upload.php
.

Файл login.php
не особенно интересен, так как PHP тут используется только для настройки компонента ActiveX, нужного для браузерных дополнений, которые стримят видео на сайте.

Файл download.php
принимает на вход название файла для скачивания, проверяет его расширение и, если такой файл найдется в папке updownload
, отправляет в ответ его содержимое.
В скрипте нет проверки названия файла, так что если кто-то вдруг решит положить в этот каталог исполняемый скрипт на PHP, то его содержимое при запросе будет скачиваться (обрати внимание на переменную $file_type
, которая будет пустой в случае неизвестного расширения).


Последний файл — upload.php
тоже оказался не без багов: в нем есть возможность отправлять файлы не только с расширением из белого списка (.dat и .DAT), но и с пустым расширением.
Вайтлист расширений задается следующей строкой.

Теперь, если значение расширения не пустое, проводится проверка на наличие расширения в массиве, который получен из $allowExt
. В качестве разделителя используется запятая.

Но если расширение пустое, исполнение не дойдет до этого условия и проверка не выполнится. Однако для эксплуатации этот баг бесполезен.
А вот следующий случайно найденный баг этого скрипта уже стоит расценить как уязвимость: здесь отсутствует проверка на длину названия файла. Казалось бы, не очень серьезная проблема, но в начале программы запускается скрипт на Bash.

Он очищает директорию updownload
от ранее загруженных туда файлов, а в интерпретаторе Bash, который входит в BusyBox, стоит ограничение на длину названия файла в 256 символов. Получается, что скрипт не сможет удалить файлы, названия которых длиннее этого значения.
Так как у upload.php
нет никакой авторизации, любой пользователь может загрузить сколько угодно файлов с именем длиннее 256 символов, и это приведет к заполнению всей памяти устройства. Другими словами, Denial of Service.
Пример загрузки файла.

И получение списка файлов в директории /updownload/
через консоль Bash.

На этом мы можем завершить изучение скриптов на PHP и перейдем к самой большой части исследования — CGI-приложениям.
Приложения CGI на IP-камере отвечают чуть ли не за все действия в администраторской веб-панели, начиная с авторизации и заканчивая обновлением устройства.
Я разделю описание работы на тестирование «невооруженным глазом» (уязвимости, для нахождения которых не нужно реверсить исполняемые файлы) и собственно реверс этих самых бинарей.
При тестировании «невооруженным глазом» нашлись две уязвимости. Первая позволяет проводить атаки подделки межсайтовых запросов (то есть CSRF). Ее суть заключается в том, что можно применить социальную инженерию и заставить администратора перейти по вредоносной ссылке. Это дает возможность выполнить почти любую команду из админского интерфейса. Например, можно сделать вот такую ссылку:
/webparam?user&action=set¶m=add&id=tester&pass=cGFzc3dvcmQ=&authority=0&t=1552491782708
Она будет создавать пользователя tester с паролем password.

Когда я изучал трафик в Burp Suite, я долго не мог найти ответ сервера, где браузеру высылаются cookie с данными авторизации (username, auth и password). Оказалось, что искал зря: эти данные выставляются на стороне клиента через код на JavaScript в файле /inc/js/ui.js
.

То есть браузер сначала делает запрос на проверку логина и пароля и, если результат положительный, сохраняет значения логина, пароля и привилегий в соответствующие cookie. А дальше эти куки используются при отправке командных запросов, например при создании нового пользователя.

Тут-то и появляется вторая уязвимость: даже если мы не отправим cookie-переменную password, сервер все равно успешно обработает наш запрос!

То есть достаточно знать логин админа (который по умолчанию — root), чтобы обойти авторизацию и совершать любые вызовы, доступные администратору в административной веб-консоли камеры! И это мы нашли, даже не изучая код приложения. Посмотрим, что же будет в самом коде.
Изучение бинарных приложений
Для изучения исполняемых файлов потребовались некоторые приготовления. А именно:
- установка статически скомпилированного отладчика GDB из публичных репозиториев на GitHub;
- установка карточки MicroSD с файловой системой VFAT (что позволяет получить дополнительное место).
Сам процесс исследования скомпилированных приложений выглядит так.
- Изучение приложения в IDA Pro.
- При необходимости — отладка приложения в GDB на самой камере через Telnet. Кстати, поскольку приложение многопоточное, пришлось каждый раз проверять нужный process id для взаимодействия с определенным потоком (поток создается до обработки запроса).
- Написание proof-of-concept для демонстрации уязвимости.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»