![](https://habrastorage.org/webt/wk/gg/bb/wkggbbfxuzftoyyqhtjr0hh6x6o.jpeg)
В 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-приложения, в которой и происходит дальнейшая обработка запроса.![](https://habrastorage.org/webt/eu/hm/yd/euhmydlkhy2r76pkvmiurftmv3a.png)
Рисунок 1. Обработка HTTP-запроса
В ходе своей работы функция main библиотеки
libipinfusionweb
вызывает функцию get_login_user
, которая по переданными значениям Cookie определяет, прошел ли пользователь аутентификацию в системе.![](https://habrastorage.org/webt/ra/-a/tl/ra-atlrdqdebpr4qgh855ykd5f8.png)
Рисунок 2. Фрагмент псевдокода функции main
Функция
get_login_user
получает значение параметра Cookie c_session
с помощью функции cookies_get_value
и сохраняет его в переменную local_e0
. Сама переменная local_e0
представляет собой массив однобайтных символов длиной 0x80 и находится на удалении 0xE0 от начала стека.![](https://habrastorage.org/webt/2s/m7/ti/2sm7tilkgmlwt5ahmjz742yonxu.png)
Рисунок 3. Фрагмент псевдокода функции get_login_user
Однако в коде функции
cookies_get_value
видно, что получаемое с помощью функции cgiCookieString
значение параметра Cookie имеет максимальную длину 0x400 байт.![](https://habrastorage.org/webt/jt/uz/xv/jtuzxvss3ihvfbtjp0vnq0ytr4c.png)
Рисунок 4. Фрагмент псевдокода функции cookies_get_value
Таким образом, при передаче параметра Cookie длиной больше 0xE0 (224) символа функция
get_login_user
сохранит значение данного параметра на свой стек, в результате чего вся информация на стеке, находящаяся за переменной local_e0
, будет перезаписана, в том числе и адрес возврата функции.Примечание: Когда одна функция вызывает другую, на стеке сохраняется адрес возврата. По этому адресу передается управление, когда вызываемая функция закончит свою работу. Соответственно, если перезаписать данный адрес, то можно получить контроль над процессом выполнения программы. Например, злоумышленник может заменить данный адрес на адрес зловредного шеллкода, размещенного в адресном пространстве программы.
Отметим, что перезапись адреса возврата происходит до проверки аутентификации, что дает возможность проэксплуатировать эту уязвимость злоумышленнику, не знающему учетных данных устройства.
Эксплуатация
Мы рассматривали несколько вариантов демонстрации возможности эксплуатации данной уязвимости. Самое простое — записать код полезной нагрузки на стек (для него остается 0x400 – 0xE0 = 800 байт, вполне достаточно для кода) и перезаписать адрес возврата адресом кода. Теоретически данный вариант был возможен, так как процессор уязвимого коммутатора не поддерживает функцию NX-бита (то есть разрешает выполнять код, расположенный где угодно, в том числе на стеке), — но на практике имел серьезные ограничения.
Процессор уязвимого коммутатора имеет архитектуру MIPS; многие из инструкций процессора данной архитектуры кодируются последовательностями байтов, содержащими нулевой байт. Запись же содержимого буфера производится до первого нулевого байта (из-за использования функции
strcpy
), поэтому необходимо использовать только операнды, не содержащие нулевого байта, что невозможно, поскольку любая полезная нагрузка использовала бы по меньшей мере несколько таких байтов.При сооружении ROP-цепочки опять бы пришлось столкнуться с ограничениями нулевого байта: в адресе ROP-гаджетов не должно быть нулей, что значительно усложняет их поиск. По большому счету, мы могли использовать только один ноль, копируемый функцией strcpy. Это накладывает ограничение на создание полноценной ROP-цепочки, и вдобавок к этому необходимых нам гаджетов было крайне мало. Однако в ходе поисков в библиотеке libipinfusionweb был найден следующий фрагмент кода:
![](https://habrastorage.org/webt/am/xs/u5/amxsu5zw9x3p9-0tfzjtu-geal8.png)
Рисунок 5. Фрагмент исполняемого кода библиотеки libipinfusionweb
При условии контроля содержимого регистра $s0 данный фрагмент кода позволяет выполнить команду ОС с помощью функции
mysystem
(изначально данная функция не имела названия, но мы ее переименовали, так как она во многом похожа на функцию system в Linux).Поскольку мы перезаписываем адрес возврата из функции get_login_user, данная функция будет выполнена до конца. В эпилоге функции get_login_user можно увидеть, что значение регистра $s0 восстанавливается из сохраненного ранее значения на стеке (по смещению 0xD8 от вершины стека). Однако к этому моменту данная область стека уже находится под нашим контролем, то есть фактически мы можем добиться контроля над содержимым регистра $s0 и выполнять таким образом произвольные команды ОС с помощью функции
mysystem
.![](https://habrastorage.org/webt/e3/k7/d9/e3k7d9mryjtpuojvi62ho7weooy.png)
Рисунок 6. Фрагмент исполняемого кода функции get_login_user
Таким образом, чтобы успешно продемонстрировать эксплуатацию данной уязвимости, нам необходимо послать в качества параметра
Cookie c_session
длинную строку, содержащую:- строковую команду ОС, которая будет впоследствии передана функции mysystem;
- адрес данной команды на стеке;
- новый адрес возврата (адрес фрагмента кода, представленного на рис. 5).
Итоговая полезная нагрузка должная выглядеть следующим образом:
![](https://habrastorage.org/webt/uz/zv/xp/uzzvxpf4lv9zws3zpnmjgdzqcyq.png)
Рисунок 7. Полезная нагрузка
К данному моменту мы уже обладали шеллом на устройстве, полученным с помощью уязвимости, для эксплуатации которой нужны были права администратора. Поэтому мы смогли получить дополнительную информацию, которая помогла нам в эксплуатации:
- ASLR на исследуемом устройстве был отключен — поэтому адреса используемого гаджета и команды ОС всегда будут одинаковыми.
![](https://habrastorage.org/webt/wi/84/v3/wi84v3-p-susaji9zxkzat8yrdq.png)
Рисунок 8. Состояние ASLR на исследуемом устройстве
- Диапазон адресов памяти, в которых мог лежать стек. Для вычисления точного адреса мы перебрали все адреса данного диапазона.
В качестве полезной нагрузки мы реализовали загрузку веб-шелла — CGI-приложения следующего содержания:
#!/bin/sh
eval $HTTP_CMD 2>&1
Так как, согласно протоколу CGI, содержимое HTTP-заголовков передается CGI-приложению в виде переменных окружения с именами HTTP_<Имя заголовка>, этот шелл с помощью команды
eval
будет выполнять содержимое HTTP-заголовка CMD. На рисунке ниже представлен результат успешной эксплуатации и выполнения команды ls с помощью загруженного шелла.![](https://habrastorage.org/webt/az/lp/um/azlpumbjd1xgx9r6q3xqhihtiqy.png)
Рисунок 9. Результат успешной эксплуатации и выполнения команды ls
Вывод
Мы продемонстрировали возможность эксплуатации данной уязвимости. Как мы уже упоминали, ее эксплуатация не требует знания пароля, и поэтому может быть выполнена даже неаутентифицированным злоумышленником.
Взлом коммутатора промышленной сети может привести к компрометации всего производства. Нарушение сетевого взаимодействия может негативно повлиять на технологический процесс вплоть до его полной остановки.
Информация об уязвимости и PoC были переданы вендору, который выпустил исправленную прошивку версии 1.34, а самой уязвимости был присвоен идентификатор CVE-2018-10731.
Ищем человека
Если у тебя есть желание заниматься подобными вещами, мы как раз ищем специалиста в нашу команду. Мы занимаемся анализом защищенности АСУ ТП (крупных объектов промышленности/энергетики/транспорта и т.д.). На каждом проекте мы ищем пути остановки или нарушения работы этих объектов. Каждый раз находим множество путей влияния на техпроцесс, исследуя промышленное железо и софт, разбирая проприетарные сетевые протоколы. Находим и репортим много зиродеев, пишем отчеты (не по ГОСТу) и делаем многое другое. Когда есть время – выступаем на ИБ и не только конференциях. Ссылка на вакансию: hh.ru/vacancy/36371389
Автор: Вячеслав Москвин, Positive Technologies
Gengenid
Мне кажется, единственный способ обеспечить защищенность АСУТП — исключить возможность того, что кто-нибудь будет эксплуатировать уязвимости АСУТП.
А все эти тесты — все равно что вычерпывать океан ложкой. Не, вы конечно наберете полное ведро и гордо предъявите его заказчику, но смысл.
DrMefistO
Security through obscurity — не работает.
Gengenid
Это скорее безопасность через изоляцию. И это работает, отлично работает. Наиболее важный метод обеспечения безопасности действительно важных вещей.
DrMefistO
А можно пример — как исключить такую возможность?
Gengenid
Физическая изоляция сетей АСУТП внутри охраняемых периметров
DrMefistO
Это не защищает от инсайда.
Gengenid
От инсайда вообще ничего не защищает. Инсайдеру не нужны уязвимости, чтобы разрушить производство. Достаточно просто отвёртки.
DrMefistO
Через использованную уязвимость инсайдера потом отследить сложнее, т.к. это зачастую отсутствие логов.
Gengenid
Отвёртки тоже ни ip ни mac адреса не имеют.
А широта и простота их применения, возможность имитации случайных повреждений вообще создают широчайшее поле для сокрытия улик.