Приветствую!

В этот раз я хотел бы рассказать о своём первом проекте в компании BI.ZONE. Дело было давно, девайс (Wisenet HRX-1620) уже не выпускается и не обслуживается, поэтому за давностью лет можно и поделиться. Заодно это предостережёт вас от использования этого устройства на объектах и поможет защититься.

Где-то на третий день работы мне вручили «посылку» с надписью «Wisenet». Внутри лежало несколько камер видеонаблюдения и одна большая коробочка:

Красивая коробочка, люблю такие
Красивая коробочка, люблю такие

Загуглив название железки, я нашёл сайт с прошивками, скачал самую свежую и полез распаковывать.

Содержимое прошивки

Основной файл с расширением .img на деле оказался обычным .tar.gz. Если его распаковать, вашему взору предстанут следующие файлики:

Содержимое архива
Содержимое архива
Содержимое каталога uptools
Содержимое каталога uptools

Большая часть файлов зашифрована (пока непонятно как), но вы обязательно заметите один интересный ELF-файл: swupgrader.

Такая схема достаточно часто используется во всяких файлах обновлений: в архив кладутся какие-то бинари, а с ними — программа или скрипт для установки.

Конечно, первым делом я открыл этот самый swupgrader в надежде найти ключ, которым была зашифрована прошивка.

Ключей там не нашлось, но обнаружилась очень подробная система логирования (классика), где у всех важных функций были указаны имена:

Приятно, когда разработчики всё логируют
Приятно, когда разработчики всё логируют
Формирование команды запуска protector
Формирование команды запуска protector

Таким образом мне удалось найти функции, которые назывались DecryptFile и DecryptImageFiles. В последнюю, кстати, передавалось имя какого-то странного бинаря protector, в сочетании с которым во второй функции формировалась команда следующего вида:

protector bin_file 1

Судя по всему, этот скрипт или программа как раз таки и расшифровывали все нужные файлы. Но, очевидно, доступа к «протектору» у меня не имелось (пока что).

Раз уж в swupgrader больше ловить нечего… Так, стоп! А как же классическая тема с подменой бинаря на свой с целью его запуска? Да, это сработало, но не сразу — процесс апгрейда почему-то падал.

Причину падения удалось выяснить немного позже (спойлер: криво собирался армовский «эльф»), поэтому я решил пойти немного другим путём: попробовать расшифровать бинари по-тупому — а вдруг там простейший XOR?

Цикличность данных - один из основных признаков блочного шифрования с помощью XOR
Цикличность данных - один из основных признаков блочного шифрования с помощью XOR
Шестнадцатеричное представление
Шестнадцатеричное представление

Внимательный реверсер обнаружит здесь повторяющийся паттерн, который может говорить только об одном: да, используется тупой XOR с одним и тем же ключом для каждого блока. Длина ключа оказалась 16 байт. Далее я сделал предположение, что в конце прошивки идут либо нули, либо FF. Соответственно, чтобы получить сам ключ, нужно поксорить повторяющиеся байты на 0xFF или взять как есть. Оказалось, первое. Так я и получил файлы прошивок.

Ну а дальше дело техники: замаунтить обнаруженную внутри JFFS2 (Journaling Flash File System version 2) и другие файлы (ext2), извлечь содержимое и провести полноценный анализ.

Что обнаружилось внутри

Конечно же, первым делом я полез смотреть, что из себя представляет protector и насколько плохо он расшифровывает файлы (спойлер: очень плохо!).

Попробуйте найти здесь ошибку:

Что-то здесь не так
Что-то здесь не так

Далее нужно было получить persistence (закрепиться на системе), чтобы отлаживать встроенные приложения в динамике и баловаться с возможными переполнениями (да, они тут тоже есть). Казалось бы, достаточно просто добавить в автостарт нужные telnet и GDB (я использовал IDA debug server), запаковать и зашифровать всё обратно — да и готово. Но нет. В самом начале, при распаковке .tar.gz, архиватор сообщил мне, что в конце архива имеются неиспользуемые данные. Посмотрим на них:

Структура завершающего блока .tar.gz
Структура завершающего блока .tar.gz

Видим структуру, которая начинается на AA CC BC BB и заканчивается на BC CA AB BC, и каких-то два дворда: 0x012A и 0x059F29D5. С первым удалось определиться достаточно быстро — это количество блоков по 0x80000. Ну а второе, как можно уже догадаться, — это контрольная сумма CRC32 (полином удалось подобрать — 0x04C11DB7).

Затем я написал пересобиралку, включил необходимые мне FTP, Telnet и отладку, после чего залил на устройство в надежде, что оно после этого заведётся. Завелось!

Ну а дальше начинается исключительно пентестерская тема. Нам понадобятся знания реверс-инжиниринга и написания ROP-цепочек, так как реверсить придётся не простой скриптец на JS, PHP или ещё на чём, а нормальный такой ELF-CGI.

Реверсим веб

Настройки доступа по HTTP(s)
Настройки доступа по HTTP(s)

Если честно, я достаточно слаб в безопасности web-приложений, но зато неплох в реверсе бинарей. Поэтому мне проще найти уязвимости именно в них. Но, «бурпом» пару раз я таки пользовался, поэтому запустил его, открыл веб-интерфейс видеорегистратора и стал тыкать во всякие менюшки и кнопочки.

Выяснилось, что всё в железке работает на CGI-скриптах, которые общаются между собой посредством линуксовых очередей сообщений. Я не буду сильно вдаваться во взаимодействие сервисов на устройстве, а сразу перейду к тому, как мне удалось найти уязвимость.

Уязвимость номер 1

Взяв самый часто используемый CGI (по мнению того же Burp), я стал передавать в его параметры длинные строки — авось что-то упадёт. И оно упало! Далее я выполнил поиск по строкам бинаря, нашёл место, где этот аргумент парсился, а там обнаружил вот такой интересный кусок кода:

А 80 байт точно хватит?
А 80 байт точно хватит?

Здесь содержимое параметра msubmenu считывается между символов & и =, после чего записывается в буфер размером 80 байт. Эксплуатируемость этой уязвимости не очень высокая: в GET-запрос нельзя запихнуть большинство символов, а urldecode на данном этапе ещё не происходит. Поэтому перейдём к уязвимости номер 2.

Уязвимость номер 2

Эта штука посерьёзнее: ограничений на символы практически нет, за исключением 0x00 и ещё некоторых. Взглянем на неё:

Парсим HTTP-заголовки
Парсим HTTP-заголовки

Видим, что из хедера HTTP-авторизации читается содержимое переданного в функцию имени поля в аргументе name. Всё, что находится между символами двойных кавычек, будет скопировано по указателю dest. Посмотрим на место вызова этой функции:

Аргументов много, каждый уязвим
Аргументов много, каждый уязвим

Сначала очищается буфер tmp размером 0x800, а потом передаётся в функцию get_http_auth_key(). Сам же буфер лежит, конечно же, на стеке. Вот он:

Буфер для хранения ROP-цепочек
Буфер для хранения ROP-цепочек

Эксплуатируемость налицо (а точнее, на стек). Пиши ROP-цепочку и вперёд. Только вот есть одно НО: включённый ASLR.

Обходил я его довольно просто. Под отладкой я заметил, что диапазон адресов, по которому грузится libc, достаточно ограниченный, а значит, проще будет его забрутфорсить. Почему это сработает? Всё потому, что при обращении к CGI-скрипту lighttpd его поднимает, если тот ещё не запущен. Поэтому даже если наша цепочка пойдёт не туда, всё упадёт, а потом поднимется. Звучит крайне неплохо. Так я смог выполнять любые команды по сети.

Ищем нужные ROP-цепочки
Ищем нужные ROP-цепочки

Добиваем железку

Далее мне стало интересно, как и где хранится пароль от учётки admin. Посмотрев, какие файлы обновляются при изменении пароля, я выяснил, что мне нужен nvrConfig.xml, который располагается на внешнем HDD (видимо, на случай восстановления). Данные зашифрованы с помощью AES-CBC и пароля #5i#$@bmb$mf75ads89egs@#$bcx798! плюс Base64. Чтобы менять их удалённо, нужно было всего лишь повозиться с магией sed и заменой по регекспу, после чего я мог менять пароль динамически.

Общение с вендором

Конечно же, мы уведомили разработчика девайса об уязвимости. Меня даже попросили прислать PoC, что я и сделал.

Спустя время мне ответили приблизительно следующее: «Спасибо, что обнаружили уязвимость. Вообще наши внутренние тестировщики уже нашли её год назад, но, так как устройство уже находится в стадии End of Life, обновления мы не выпускаем».

В общем, единственный здравый совет здесь: ограничить доступ к HTTP-портам Wisenet HRX-1620 с недоверенных MAC- и IP-адресов. А лучше вообще выключить доступ по HTTP или перестать пользоваться устройством в пользу другого.

Комментарии (5)


  1. SSLHTML
    04.10.2023 14:34
    +2

    Ваши изыскания и наработки просто прекрасны и являются отличным материалом для моего обучения, без специальных знаний. Спасибо за подробное пояснение ваших шагов.

    Очень хотелось бы предложить написать самоучитель для начинающих реверсёров или "палетку", т.е. мануал для действий при попадании в тупик.

    Также интересно, вы реверсите в основном на Unix-системах или всё-таки на Win?

    Спасибо!


    1. DrMefistO Автор
      04.10.2023 14:34

      Если вопрос про инструментарий, то IDA под виндой, Ghidra под любыми работает. Если о целевых платформах, то попадается всякое. Предпочтений нет.

      Писать самоучитель желания нет, т.к. всё и так в сети: видео, статьи - брать да изучать:)


      1. SSLHTML
        04.10.2023 14:34
        +1

        Понял вас, Владимир. Просто есть специалисты, которые со скрипом "работают" под Win. Вы-универсал. Прошу прощения за глупый вопрос, а IDA не перекрывает возможности Ghidra? Я сейчас пытаюсь осваивать Radare2. Полагаю, что вы слышали о нём.

        Вот, как раз я и изучаю по таким вот постам исследователей и видеороликам в youtube. Хочется, чтобы они были описаны также, как делаете вы. Это удобно и не утомляет, в отличии от занудного бубнения англоязычных исследователей.


        1. DrMefistO Автор
          04.10.2023 14:34

          У Иды нет возможности написать свой собственный процессорный модуль и декомпилировать что угодно, а у Гидры - есть. Под PE-файлы Ида конечно лучшая. Я как-то писал статью про сравнение Иды и Гидры.


          1. SSLHTML
            04.10.2023 14:34
            +1

            Спасибо! Сейчас поищу и ознакомлюсь с вашей статьёй. Буду дальше изучать хотя бы примитивные возможности IDA и Ghidra.