Около года назад в моем распоряжении оказалась IP-камера Grandstream GXV3611_HD, у которой были проблемы с Power over Ethernet (PoE), технологией, позволяющей передавать удаленному устройству, наряду с данными, электрическую энергию.
Я решил попробовать устранить этот недочет. Поскольку я не настолько хорошо разбираюсь в электронных схемах, мне пришлось посмотреть в блоге EEVblog множество видео по теме ремонта и устранения проблем у IP-камер. В конечно итоге, проблему решить не удалось, но зато получилось найти уязвимость, связанную с sql-инъекцией, и недокументированную команду, позволяющую запустить службу telnet и удаленно залогиниться с правами суперпользователя.
Содержание:
1. Аппаратная часть
2. Коммуникация с камерой и получение шелла
3. Повторное получение шелла
4. Поиск уязвимостей
5. Получение удаленного шелла
Аппаратная часть
Камера разобрана, а на рисунке ниже показано 10×2 пинов. Я предполагал, что эти контакты предназначались для отладки и были последовательными.
Рисунок 1: Внутреннее устройство IP-камеры
Я подключил свой мультиметр ко всем пинам, что выяснить, присутствует ли где-то напряжение более 5 вольт, поскольку мой логический анализатор Saleae может оперировать с напряжениями не выше этого значения. На контактах не было напряжения более 5 вольт, и я решил подключить логический анализатор к случайным пинам.
Рисунок 2: Подключение логического анализатора
После включения камеры я заметил появление данных на одном из каналов и предположил, что этот разъем – последовательный.
Рисунок 3: Информация на одном из каналов, отображаемая на экране логического анализатора
Коммуникация с камерой и получение шелла
Я подсоединил последовательный адаптер (FTDI) и подключился к камере при помощи терминала Putty. Скорость в бодах определялась на основе выходного сигнала на логическом анализаторе и была примерно 115200 бод, что является одним из стандартных значений.
Рисунок 4: Предварительная настройка терминала Putty
Я подключился к пину TX и смог получить данные с камеры в терминале.
Рисунок 5: Процесс загрузки IP-камеры
Я выбрал пин, находящийся над TX, чтобы убедиться, сможет ли этот контакт принимать информацию. Так и оказалось. Единственный пригодный способ для нахождения пина RX, который я знаю, — отслеживание дорожек на плате.
Рисунок 6: Подключение к пину RX
Я перезагрузил камеру и остановил автозагрузку.
Рисунок 7: Остановленный процесс автозагрузки
В камере был загрузчик U-Boot, используемый во многих подобных устройствах.
Я посмотрел видео под названием «Hacking 20 devices in 45 minutes» с конференции Defcon, где рассказывалось о получения шелла при помощи изменения опции init в U-Boot, и поступил схожим образом.
Команда printenv выводит все текущие настройки загрузки.
Рисунок 8: Текущие настройки загрузки
Команда setenv позволяет изменять значение опций загрузки. Я установил init=/bin/sh, чтобы получить доступ к шеллу.
Рисунок 9: Изменение настроек загрузки
Команда bootm используется для загрузки из памяти. Адрес берется из выходных данных команды printenv.
Рисунок 10: Возобновление загрузки после изменения настроек
Камера загрузилась, и я получил доступ к шеллу busybox:
Повторное получение шелла
Я начал просматривать файловую систему и заметил пустые папки. Подобное случилось потому, что я модифицировать процесс загрузки.
Рисунок 11: Просмотр содержимого некоторых папок
Я снова перезагрузил камеру и позволил устройству загрузиться в обычном режиме. После загрузки мне было предложено ввести логин и пароль.
Рисунок 12: Перезагрузка камеры в обычном режиме
Поскольку логина я не знал, то перезагрузил камеру и снова изменил init на /bin/sh, а затем выгрузил хеши.
Рисунок 13: Содержимое файла passwd
Пароль оказался пустым.
Рисунок 14: Поиск паролей к найденным хешам
Я загрузил камеру в обычном режиме и залогинился. Теперь я мог просматривать содержимое папок, которые ранее были пустыми.
Рисунок 15: Содержимое некоторых папок после ввода корректного логина/пароля
Поиск уязвимостей
После получения последовательного шелла и доступа к файловой системе я начал поиск уязвимостей, связанных с удаленным доступом, посредством исследования процессов, запущенных на устройстве.
Рисунок 16: Перечень процессов, запущенных на устройстве
В перечне на рисунке выше отмечены запущенные процессы webs и cli. Webs – веб-сервер, cli – сервер, связанный с настройкой telnet (telnet configuration server).
Было решено проанализировать оба этих процесса в IDA.
Я начал с cli поскольку этот процесс по объему оказался сильно меньше, чем webs. После подключения к серверу telnet и авторизации, нам становится доступен перечень команд.
Рисунок 17: Перечень команд, доступных в telnet
Я загрузил бинарник в IDA и стал искать строку «Username:»
Рисунок 18: Результаты поиска по строке «Username:»
Во время просмотра дизассемблированной версии я нашел sql-выражения. Я посмотрел в окно со строками и функциями и обнаружил, что программа использует sqlite.
Рисунок 19: Окна со строками и функциями
Я скопировал файлы cli, webs и system.db на свой компьютер. System.db представляет собой базу данных в sqlite, в которой есть таблица user с именами пользователей, паролей, привилегий и статусами учетных записей.
Рисунок 20: Содержимое таблицы user
На рисунке ниже показана часть дизассемблированного кода, имеющая отношения к SQL-запросу для взаимодействия с таблицей user.
Рисунок 21: Часть кода, взаимодействующего с таблицей user
Часть кода, связанная с аутентификацией и подверженная sql-инъекции.
Рисунок 22: Уязвимость, связанная с sql-инъекцией
В обычном режиме запрос выполняется так:
На входе = Имя пользователя
Итоговый запрос = select * from users where name = ' Имя пользователя ';
Мы можем сделать так:
На входе = ';на собственный sql запрос;--
Итоговый запрос = select * from users where name = ''; на собственный sql запрос;--';
Я открыл базу данных system.db в SQLite Database Browser и опробовал некоторые выражения перед выполнением запросов непосредственно внутри камеры.
Первой моей мыслью было изменить пароль административного аккаунта.
Рисунок 23: Выполнение запроса на изменение пароля у пользователя admin
Трюк сработал.
Однако внутри камеры при выполнении запроса возникла ошибка.
Рисунок 24: Обрыв соединения после выполнение запроса
Соединение обрывалось, если в поле Username передавалось слишком много данных.
Я вернулся в SQLite Database Browser, и после некоторых экспериментов появился следующий запрос: '; update user set password='a';--
Рисунок 25: Результат выполнения обновленного запроса
Запрос, показанный выше, позволяет выставить пароль «a» всем пользователям таблицы user.
Сейчас я понимаю, что мог бы изменять поля privilege и disable, чтобы лучше замаскироваться. В таком случае я мог бы залогиниться при помощи анонимной учетной записи.
Получение удаленного шелла
Я продолжил исследование файла cli и заметил команду !#/, которая не присутствовала в справке, когда я вводил команду 'help'.
Рисунок 26: Недокументированная команда !#/
На рисунке ниже показано место, куда происходит переход:
Рисунок 27: Участок кода, выполняемый после команды !#/
Попросту говоря, происходит выполнение telnetd на порту 20000. После sql-инъекции и получения доступа к конфигурационному меню командной строки я могу выполнить команду !#/ и запустить telnetd. Далее я могу авторизироваться удаленно при помощи логина root и пустого пароля.
Рисунок 28: Удаленное подключение к камере