Продолжаем разбирать уязвимости промышленных коммутаторов: выполняем произвольный код без пароля

в 16:12, , рубрики: Блог компании Positive Technologies, взлом, информационная безопасность, промышленные коммутаторы, уязвимости

В Positive Research 2019 мы разобрали протокол управления промышленными коммутаторами Moxa. В этот раз мы продолжим эту тему и подробно разберем уязвимость CVE-2018-10731 в коммутаторах Phoenix Contact моделей линейки FL SWITCH 3xxx, FL SWITCH 4xxx, FL SWITCH 48xx, выявленную нашими экспертами. Данная уязвимость, обнаруженная в веб-интерфейсе устройства, позволяет выполнить произвольный код без знания учетных данных устройства и оценена в 9 из 10 баллов по шкале CVSS версии 3.

Первый взгляд

Упомянутые выше устройства работают под управлением Linux, а для их настройки можно использовать веб-интерфейс. Как и на многих других IoT-устройствах, бытовых и индустриальных, веб-интерфейс состоит из множества CGI-приложений, обрабатывающих HTTP-запросы пользователя. В нашем случае CGI-приложения активно используют библиотеку cgic, облегчающую работу с HTTP-запросами, а функции этой библиотеки встроены в разделяемую библиотеку libipinfusionweb.so, расположенную в файловой системе устройства.

При обработке HTTP-запроса веб-сервер передает данные запроса пользователя CGI-приложению как набор переменных среды. Первоначальная их обработка производится функцией main библиотеки libipinfusionweb. Далее функция main вызывает функцию cgiMain CGI-приложения, в которой и происходит дальнейшая обработка запроса.

Продолжаем разбирать уязвимости промышленных коммутаторов: выполняем произвольный код без пароля - 1

Рисунок 1. Обработка HTTP-запроса

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

Продолжаем разбирать уязвимости промышленных коммутаторов: выполняем произвольный код без пароля - 2

Рисунок 2. Фрагмент псевдокода функции main

Функция get_login_user получает значение параметра Cookie c_session с помощью функции cookies_get_value и сохраняет его в переменную local_e0. Сама переменная local_e0 представляет собой массив однобайтных символов длиной 0x80 и находится на удалении 0xE0 от начала стека.

Продолжаем разбирать уязвимости промышленных коммутаторов: выполняем произвольный код без пароля - 3

Рисунок 3. Фрагмент псевдокода функции get_login_user

Однако в коде функции cookies_get_value видно, что получаемое с помощью функции cgiCookieString значение параметра Cookie имеет максимальную длину 0x400 байт.

Продолжаем разбирать уязвимости промышленных коммутаторов: выполняем произвольный код без пароля - 4

Рисунок 4. Фрагмент псевдокода функции cookies_get_value

Таким образом, при передаче параметра Cookie длиной больше 0xE0 (224) символа функция get_login_user сохранит значение данного параметра на свой стек, в результате чего вся информация на стеке, находящаяся за переменной local_e0, будет перезаписана, в том числе и адрес возврата функции.

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

Отметим, что перезапись адреса возврата происходит до проверки аутентификации, что дает возможность проэксплуатировать эту уязвимость злоумышленнику, не знающему учетных данных устройства.

Эксплуатация

Мы рассматривали несколько вариантов демонстрации возможности эксплуатации данной уязвимости. Самое простое — записать код полезной нагрузки на стек (для него остается 0x400 – 0xE0 = 800 байт, вполне достаточно для кода) и перезаписать адрес возврата адресом кода. Теоретически данный вариант был возможен, так как процессор уязвимого коммутатора не поддерживает функцию NX-бита (то есть разрешает выполнять код, расположенный где угодно, в том числе на стеке), — но на практике имел серьезные ограничения.

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

При сооружении ROP-цепочки опять бы пришлось столкнуться с ограничениями нулевого байта: в адресе ROP-гаджетов не должно быть нулей, что значительно усложняет их поиск. По большому счету, мы могли использовать только один ноль, копируемый функцией strcpy. Это накладывает ограничение на создание полноценной ROP-цепочки, и вдобавок к этому необходимых нам гаджетов было крайне мало. Однако в ходе поисков в библиотеке libipinfusionweb был найден следующий фрагмент кода:

Продолжаем разбирать уязвимости промышленных коммутаторов: выполняем произвольный код без пароля - 5

Рисунок 5. Фрагмент исполняемого кода библиотеки libipinfusionweb

При условии контроля содержимого регистра $s0 данный фрагмент кода позволяет выполнить команду ОС с помощью функции mysystem (изначально данная функция не имела названия, но мы ее переименовали, так как она во многом похожа на функцию system в Linux).

Поскольку мы перезаписываем адрес возврата из функции get_login_user, данная функция будет выполнена до конца. В эпилоге функции get_login_user можно увидеть, что значение регистра $s0 восстанавливается из сохраненного ранее значения на стеке (по смещению 0xD8 от вершины стека). Однако к этому моменту данная область стека уже находится под нашим контролем, то есть фактически мы можем добиться контроля над содержимым регистра $s0 и выполнять таким образом произвольные команды ОС с помощью функции mysystem.

Продолжаем разбирать уязвимости промышленных коммутаторов: выполняем произвольный код без пароля - 6

Рисунок 6. Фрагмент исполняемого кода функции get_login_user

Таким образом, чтобы успешно продемонстрировать эксплуатацию данной уязвимости, нам необходимо послать в качества параметра Cookie c_session длинную строку, содержащую:

  • строковую команду ОС, которая будет впоследствии передана функции mysystem;
  • адрес данной команды на стеке;
  • новый адрес возврата (адрес фрагмента кода, представленного на рис. 5).

Итоговая полезная нагрузка должная выглядеть следующим образом:

Продолжаем разбирать уязвимости промышленных коммутаторов: выполняем произвольный код без пароля - 7

Рисунок 7. Полезная нагрузка

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

  • ASLR на исследуемом устройстве был отключен — поэтому адреса используемого гаджета и команды ОС всегда будут одинаковыми.

Продолжаем разбирать уязвимости промышленных коммутаторов: выполняем произвольный код без пароля - 8

Рисунок 8. Состояние ASLR на исследуемом устройстве

  • Диапазон адресов памяти, в которых мог лежать стек. Для вычисления точного адреса мы перебрали все адреса данного диапазона.

В качестве полезной нагрузки мы реализовали загрузку веб-шелла — CGI-приложения следующего содержания:

#!/bin/sh
eval $HTTP_CMD 2>&1

Так как, согласно протоколу CGI, содержимое HTTP-заголовков передается CGI-приложению в виде переменных окружения с именами HTTP_<Имя заголовка>, этот шелл с помощью команды eval будет выполнять содержимое HTTP-заголовка CMD. На рисунке ниже представлен результат успешной эксплуатации и выполнения команды ls с помощью загруженного шелла.

Продолжаем разбирать уязвимости промышленных коммутаторов: выполняем произвольный код без пароля - 9

Рисунок 9. Результат успешной эксплуатации и выполнения команды ls

Вывод

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

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

Информация об уязвимости и PoC были переданы вендору, который выпустил исправленную прошивку версии 1.34, а самой уязвимости был присвоен идентификатор CVE-2018-10731.

Ищем человека

Если у тебя есть желание заниматься подобными вещами, мы как раз ищем специалиста в нашу команду. Мы занимаемся анализом защищенности АСУ ТП (крупных объектов промышленности/энергетики/транспорта и т.д.). На каждом проекте мы ищем пути остановки или нарушения работы этих объектов. Каждый раз находим множество путей влияния на техпроцесс, исследуя промышленное железо и софт, разбирая проприетарные сетевые протоколы. Находим и репортим много зиродеев, пишем отчеты (не по ГОСТу) и делаем многое другое. Когда есть время – выступаем на ИБ и не только конференциях. Ссылка на вакансию: hh.ru/vacancy/36371389

Автор: Вячеслав Москвин, Positive Technologies

Автор: ptsecurity

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js