Приветствую!
В этот раз я хотел бы рассказать о своём первом проекте в компании BI.ZONE. Дело было давно, девайс (Wisenet HRX-1620
) уже не выпускается и не обслуживается, поэтому за давностью лет можно и поделиться. Заодно это предостережёт вас от использования этого устройства на объектах и поможет защититься.
Где-то на третий день работы мне вручили «посылку» с надписью «Wisenet». Внутри лежало несколько камер видеонаблюдения и одна большая коробочка:
Загуглив название железки, я нашёл сайт с прошивками, скачал самую свежую и полез распаковывать.
Содержимое прошивки
Основной файл с расширением .img
на деле оказался обычным .tar.gz
. Если его распаковать, вашему взору предстанут следующие файлики:
Большая часть файлов зашифрована (пока непонятно как), но вы обязательно заметите один интересный ELF-файл: swupgrader
.
Такая схема схема достаточно часто используется во всяких файлах обновлений: в архив кладутся какие-то бинари, а с ними — программа или скрипт для установки.
Конечно, первым делом я открыл этот самый swupgrader
в надежде найти ключ, которым была зашифрована прошивка.
Ключей там не нашлось, но обнаружилась очень подробная система логирования (классика), где у всех важных функций были указаны имена:
Таким образом мне удалось найти функции, которые назывались DecryptFile
и DecryptImageFiles
. В последнюю, кстати, передавалось имя какого-то странного бинаря protector
, в сочетании с которым во второй функции формировалась команда следующего вида:
protector bin_file 1
Судя по всему, этот скрипт или программа как раз таки и расшифровывали все нужные файлы. Но, очевидно, доступа к «протектору» у меня не имелось (пока что).
Раз уж в swupgrader
больше ловить нечего… Так, стоп! А как же классическая тема с подменой бинаря на свой с целью его запуска? Да, это сработало, но не сразу — процесс апгрейда почему-то падал.
Причину падения удалось выяснить немного позже (спойлер: криво собирался армовский «эльф»), поэтому я решил пойти немного другим путём: попробовать расшифровать бинари по-тупому — а вдруг там простейший XOR?
Внимательный реверсер обнаружит здесь повторяющийся паттерн, который может говорить только об одном: да, используется тупой XOR с одним и тем же ключом для каждого блока. Длина ключа оказалась 16
байт. Далее я сделал предположение, что в конце прошивки идут либо нули, либо FF
. Соответственно, чтобы получить сам ключ, нужно поксорить повторяющиеся байты на 0xFF
или взять как есть. Оказалось, первое. Так я и получил файлы прошивок.
Ну а дальше дело техники: замаунтить обнаруженную внутри JFFS2
(Journaling Flash File System version 2) и другие файлы (ext2
), извлечь содержимое и провести полноценный анализ.
Что обнаружилось внутри
Конечно же, первым делом я полез смотреть, что из себя представляет protector
и насколько плохо он расшифровывает файлы (спойлер: очень плохо!).
Попробуйте найти здесь ошибку:
Далее нужно было получить persistence (закрепиться на системе), чтобы отлаживать встроенные приложения в динамике и баловаться с возможными переполнениями (да, они тут тоже есть). Казалось бы, достаточно просто добавить в автостарт нужные telnet
и GDB
(я использовал IDA debug server
), запаковать и зашифровать всё обратно — да и готово. Но нет. В самом начале, при распаковке .tar.gz
, архиватор сообщил мне, что в конце архива имеются неиспользуемые данные. Посмотрим на них:
Видим структуру, которая начинается на AA CC BC BB
и заканчивается на BC CA AB BC
, и каких-то два дворда: 0x012A
и 0x059F29D5
. С первым удалось определиться достаточно быстро — это количество блоков по 0x80000
. Ну а второе, как можно уже догадаться, — это контрольная сумма CRC32
(полином удалось подобрать — 0x04C11DB7
).
Затем я написал пересобиралку, включил необходимые мне FTP, Telnet и отладку, после чего залил на устройство в надежде, что оно после этого заведётся. Завелось!
Ну а дальше начинается исключительно пентестерская тема. Нам понадобятся знания реверс-инжиниринга и написания ROP-цепочек, так как реверсить придётся не простой скриптец на JS, PHP или ещё на чём, а нормальный такой ELF-CGI.
Реверсим веб
Если честно, я достаточно слаб в безопасности web-приложений, но зато неплох в реверсе бинарей. Поэтому мне проще найти уязвимости именно в них. Но, «бурпом» пару раз я таки пользовался, поэтому запустил его, открыл веб-интерфейс видеорегистратора и стал тыкать во всякие менюшки и кнопочки.
Выяснилось, что всё в железке работает на CGI-скриптах, которые общаются между собой посредством линуксовых очередей сообщений. Я не буду сильно вдаваться во взаимодействие сервисов на устройстве, а сразу перейду к тому, как мне удалось найти уязвимость.
Уязвимость номер 1
Взяв самый часто используемый CGI (по мнению того же Burp), я стал передавать в его параметры длинные строки — авось что-то упадёт. И оно упало! Далее я выполнил поиск по строкам бинаря, нашёл место, где этот аргумент парсился, а там обнаружил вот такой интересный кусок кода:
Здесь содержимое параметра msubmenu
считывается между символов &
и =
, после чего записывается в буфер размером 80
байт. Эксплуатируемость этой уязвимости не очень высокая: в GET-запрос нельзя запихнуть большинство символов, а urldecode
на данном этапе ещё не происходит. Поэтому перейдём к уязвимости номер 2.
Уязвимость номер 2
Эта штука посерьёзнее: ограничений на символы практически нет, за исключением 0x00
и ещё некоторых. Взглянем на неё:
Видим, что из хедера HTTP-авторизации читается содержимое переданного в функцию имени поля в аргументе name
. Всё, что находится между символами двойных кавычек, будет скопировано по указателю dest
. Посмотрим на место вызова этой функции:
Сначала очищается буфер tmp
размером 0x800
, а потом передаётся в функцию get_http_auth_key()
. Сам же буфер лежит, конечно же, на стеке. Вот он:
Эксплуатируемость налицо (а точнее, на стек). Пиши ROP-цепочку и вперёд. Только вот есть одно НО: включённый ASLR
.
Обходил я его довольно просто. Под отладкой я заметил, что диапазон адресов, по которому грузится libc,
достаточно ограниченный, а значит, проще будет его забрутфорсить. Почему это сработает? Всё потому, что при обращении к CGI-скрипту lighttpd
его поднимает, если тот ещё не запущен. Поэтому даже если наша цепочка пойдёт не туда, всё упадёт, а потом поднимется. Звучит крайне неплохо. Так я смог выполнять любые команды по сети.
Добиваем железку
Далее мне стало интересно, как и где хранится пароль от учётки admin
. Посмотрев, какие файлы обновляются при изменении пароля, я выяснил, что мне нужен nvrConfig.xml
, который располагается на внешнем HDD (видимо, на случай восстановления). Данные зашифрованы с помощью AES-CBC
и пароля #5i#$@bmb$mf75ads89egs@#$bcx798!
плюс Base64
. Чтобы менять их удалённо, нужно было всего лишь повозиться с магией sed
и заменой по регекспу, после чего я мог менять пароль динамически.
Общение с вендором
Конечно же, мы уведомили разработчика девайса об уязвимости. Меня даже попросили прислать PoC, что я и сделал.
Спустя время мне ответили приблизительно следующее: «Спасибо, что обнаружили уязвимость. Вообще наши внутренние тестировщики уже нашли её год назад, но, так как устройство уже находится в стадии End of Life, обновления мы не выпускаем».
В общем, единственный здравый совет здесь: ограничить доступ к HTTP-портам Wisenet HRX-1620
с недоверенных MAC- и IP-адресов. А лучше вообще выключить доступ по HTTP или перестать пользоваться устройством в пользу другого.