В современном мире нас окружают «умные» устройства, в том или ином виде представляющие собой маленький компьютер. С точки зрения интеграции с современным оборудованием, будь то бытовая электроника и умные дома, медицинское и банковское оборудование, развлекательные системы и промышленное оборудование — все это представляет собой встраиваемые системы (embedded-устройства).
«Умные» гаджеты очень плотно влились в нашу жизнь, и их число из года в год растёт благодаря их низкой стоимости, удобству и простоте в использовании, в том числе через Интернет. Однако за рядом плюсов скрываются минусы, невидимые на первый взгляд рядовому пользователю.
В одном из докладов «Очной ставки» NeoQUEST-2017 рассматривался обширный класс embedded-устройств — IP-камеры, и обсуждались общие подходы к анализу встроенного программного обеспечения. В данной статье мы более подробно остановимся на исследовании камеры Hikvision DS-2CD2432F-IW, ее прошивки и механизмов защиты от встраивания и модификации, используемых производителем.
Знакомство
Заходим на web-интерфейс и изучаем функционал: узнаем версию ПО, активируем сетевые службы (SSH, SNTP, FTP, ...). Камера нам досталась с довольно старенькой прошивкой 5.3.0. Естественно, от версии к версии вендор патчит не только функционал устройства, но и проблемы с безопасностью. На момент написания статьи самая новая прошивка для данной камеры имеет версию 5.4.5.
Для просмотра live-видео с камеры необходим плагин для браузера. Один из плагинов хранится на самой камере — скачаем его, возможно, он нам еще пригодится.
Подключаемся по SSH к устройству и видим CLI c неким набором команд, с помощью которых можно узнать текущие настройки камеры, задать свои и узнать гораздо больше информации об устройстве, чем через web-интерфейс.
Проверяем команды и и с помощью одной из них узнаем информацию об ОС, ядре ОС, архитектуре процессора, файловой системе, размерах оперативной памяти, разделах физической памяти и т.д.
Поиск информации
Важным навыком для исследователя является умение искать информацию — ведь, скорее всего, кто-то уже делал это до тебя. Ответы на многие вопросы можно найти на тематических форумах или просто в поисковых машинах. Например, зайдем на форум, посвящённый различным камерам, регистраторам и т.д.
Немного сёрфим и находим, как «провалиться» ещё глубже, а именно в root-shell, где доступны команды, «порезанные» в CLI, и файловая система!
Уже сам доступ к root-shell позволит нам получить списки отрытых портов и процессов, извлечь исполняемые файлы из файловой системы для изучения и запустить свои собственные файлы. Импортировать/экспортировать те и другие можно с помощью SD-карты.
Еще на этапе знакомства с камерой в web-интерфейсе был замечена вкладка RS232 . На форуме сразу же обнаружился тред про то, как этот интерфейс можно использовать.
Подключив UART к камере и задав нужные параметры для minicom, получаем возможность читать весь лог загрузки камеры, останавливать загрузку устройства и «проваливаться» в bootloader! Видим, что лог довольно большой, и в нем отображаются запускаемые процессы, пройденные проверки (пока нам неизвестные), ошибки и прочая системная информация.
Не менее интересен сам boot и его команды, позволяющие считывать и записывать данные в физическую память устройства, обновлять камеру через tftp-сервер (если вы вдруг превратили ее в «кирпич»), получать ещё более подробную информацию о внутренностях камеры.
Структура прошивки
Header
Немного изучив саму камеру, приступаем к исследованию образа прошивки. Скачиваем образ, отмечаем, что прошивка представляет собой неизвестный файл с расширением «.dav». Пробуем что-нибудь увидеть в hex-редакторе.
Как правило, любой образ прошивки, скорее всего, начинается с заголовка, в котором содержатся метаданные о самом образе и о поддерживаемом устройстве. Из предыдущего рисунка видим нечто, похожее на заголовок. Попробуем «скормить» файл прошивки binwalk . В первых ~130тыс байт ничего не найдено, а это говорит о том, что, скорее всего, этот блок зашифрован.
Идём «курить» уже упомянутый форум и натыкаемся на это. Оказывается, заголовок файла прошивки действительно зашифрован байт-в-байт. Блочное шифрование реализовано на основе операции XOR. Ключ операции = «BA CD BC FE D6 CA DD D3 BA B9 A3 AB BF CB B5 BE». Перед каждым следующим xor`ом ключ сдвигается циклично влево на байт. Пишем небольшой скрипт, чтобы расшифровать заголовок. Пока в результате получаем, предположительно, имена каких-то файлов и набор неизвестных байт.
Попробуем сопоставить имеющуюся информацию (размер и версия образа, лог binwalk`a и т.д.) с неизвестными данными в заголовке. Например, можно увидеть размер образа или смещение .gz-архива, которое также определил binwalk. По порядку байт можно сделать вывод, что это little-endian.
Вспомним про плагин для браузера, который мы скачали ещё на этапе знакомства. Его название фигурирует в заголовке. Сразу после его смещения в составе образа видим его размер.
Уже известно, что структура, связанная с файлами, содержит имя файла (32 байта), его смещение (4 байта) и размер (4 байта). Неизвестными пока остаются ещё 4 байта информации до следующей структуры. Скорее всего, это контрольная сумма. Осталось узнать, по какому алгоритму она рассчитывается. Так как длина всего 4 байта, можно смело сказать, что это не результат какой-то популярной хеш-функции (MD5(), SHA*()). Можно предположить, что используется CRC32(). Пробуем — мимо, суммы не сходятся.
После нескольких предположений была замечена любопытная зависимость: чем больше размер файла, тем больше его контрольная сумма. Это натолкнуло на мысль сложить последовательно байты в одном из файлов. И действительно, алгоритм вычисления контрольной суммы довольно прост и представляет собой последовательное сложение байт. Точно так же вычисляется контрольная сумма заголовка — она находится в самом начале, рядом с его размером.
Расшифрованный заголовок содержит информацию:
- о самом образе: о его версии, сборке, размере;
- об устройстве;
- о файлах в составе прошивки: имя, смещение, размер и контрольная сумма.
Подробная структура заголовка представлена ниже:
Теперь, когда структура заголовка полностью известна, нетрудно написать скрипт, который парсит заголовок и распаковывает образ на отдельные файлы (ссылки на исходники программ, реализующих распаковку исходного образа прошивки и сборку нового образа — в конце статьи!). И вот их-то мы внимательно изучим!
hroot.img
Исследуем файлы, которые входят в состав образа. О назначении одних становится понятно уже из названия, а о назначении других остаётся пока что остается только догадываться… В глаза сразу бросается файл с расширением «.img», скорее всего, это образ файловой системы. Примонтировать его сразу не получается. «Скормим» binwalk`у и увидим «.gz» архив, но лишь с 64 байта, а значит, образ имеет свой собственный заголовок.
Как правило, заголовок образа файловой системы содержит в себе контрольную сумму образа, его размер и адрес загрузки в оперативную память устройства. В нашем случае контрольная сумма CRC32(). Также добавлены «magic bytes» KDMR (RMDK, ramdisk magic). Оставшиеся байты заголовка заполнены нулями.
initrun.sh
Уже по названию можно предположить, что скрипт запускается при включении устройства. Изучив его, понимаем, что с его помощью создаются нужные директории, распаковываются архивы, копируются файлы, запускаются бинарные файлы, удаляются ненужные, подгружаются модули ядра, монтируются нужные разделы, создаются символические ссылки — все это происходит в оперативной памяти.
davinci.tar.gz
Казалось бы, lib_so.tar.gz — архив с динамическими библиотеками ничем не примечателен, но внутри архива среди библиотек затерялся исполняемый бинарный файл daemon_fsp_app. Странно, что его поместили в данный архив, хотя остальные бинарники были просто конкатенированы с остальными файлами в составе образа. Что ж, придется его зареверсить!
Оказалось, что этот бинарный файл дешифрует, запускает и удаляет, оставляя висеть в процессах, один из файлов в составе прошивки, а именно, davinci.tar.gz. Это самый интересный файл — зашифрованный исполняемый файл davinci, реализующий основную логику работы устройства. Он «управляет» web-сервером и проверяет импортируемый образ новой прошивки.
Проверяются контрольные суммы файлов, их смещения и размеры, количество файлов в составе прошивки, размер образа и его версия, класс совместимого устройства, контрольная сумма и размер заголовка, заголовок образа файловой системы и, конечно, «magic bytes». Обойти эти проверки не так сложно, если разобрать структуру каждого файла. Это позволит модифицировать образ или собрать собственный.
Производитель замаскировал davinci под сжатый архив tar.gz, но на самом деле тут совсем другая матрёшка (смотрим рисунок ниже)! Бинарный файл упакован в tar архив, сжат с помощью алгоритма lzma и зашифрован с помощью симметричного блочного алгоритма шифрования AES: размер блока — 128 бит, размер ключа — 256 бит, мод шифрование — ECB, а также добавлен заголовок с «magic bytes», который и проверяется при дешифровке.
Ключ шифрования формируется с помощью функции формирования ключа EVP_BytesToKey(), где используется MD5() в два прохода, парольная фраза «R0sslV53cryptor0» и соль «HangZhou». Для разных классов и серий устройств этот ключ будет отличен, серия исследуемой нами камеры — R0.
_cfgUpgSecPls
Файл зашифрован точно так же, как бинарный файл davinci, но отличается лишь ключом шифрования и тем, что дешифровкой занимается уже сам davinci. Ключ отличен из-за того, что используется другая парольная фраза для его формирования — «h@k8807H$Z5998». Её можно найти «внутри» бинарного файла davinci, там же, где и соль «HangZhou».
По структуре расшифрованного файла, показанной на рисунке ниже, можно сказать, что это второй заголовок образа — он также содержит метаданные, имя файла, его смещение, размер и контрольную сумму. Главным отличием является алгоритм расчёта контрольной суммы — тут обычный SHA() (не путать с SHA1() и прочими алгоритмами SHA()), и алгоритм шифрования самого файла. Этот файл, как и заголовок, проверяется davinci.
Остальные файлы в составе образа не так интересны, как вышеупомянутые. Их краткое описание представлено ниже в таблице.
Подведём печальные итоги...
Что тут скажешь? Большинство embedded-устройств производятся в Китае, и производители стараются сэкономить на работе программистов и на железе, вследствие чего это железо обладает крайне ограниченными вычислительными ресурсами. Как следствие — слабо реализованные механизмы безопасности для предотвращения модификации оригинального ПО (или вообще полное их отсутствие).
Более того, производитель самостоятельно может закладывать бэкдоры. И если взлом умного чайника вряд ли может нанести ощутимый ущерб (хотя с этим можно поспорить, опираясь на утверждение про то, что потенциально умный чайник может даже «положить» сайт Пентагона), то атака на более значимые embedded-системы может повлечь за собой серьёзные последствия.
Отсутствие автоматического обновления большинства устройств полностью подрывает их безопасность — обычный человек не следит за новостями в сфере информационной безопасности, пока это не затронет его лично или пока об этом не заговорят все подряд. Вряд ли домохозяйка полезет скачивать новую прошивку для своей камеры с китайского сайта.
Устройства могут не обновляться годами. Вследствие этого злоумышленники эксплуатируют доступную лазейку в своих личных интересах, начиная от банальной слежки за «жертвой» и заканчивая созданием ботнетов.
Остается только надеяться, что производители в будущем будут более ответственно относиться к безопасности своих продуктов, которые все в больших и больших количествах вливаются в нашу жизнь, внедряя в них современные и надёжные механизмы защиты от модификации и подмены ПО, например, ЭЦП.
P.S.
В процессе исследования были написаны программы распаковки исходного образа прошивки и сборки нового образа на Python. Распаковщик делит образ на отдельные файлы, входящие в его состав. Сборка учитывает те поля, которые проверяются при загрузке на устройство, что позволяет модифицировать любой из файлов в составе образа (без добавления нового файла в состав образа) и собрать новый файл прошивки. Для сборки требуется уже распакованный образ.
Автор: NWOcs