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

Продолжаем разбирать уязвимости промышленных коммутаторов: выполняем произвольный код без пароля
В 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. Обработка HTTP-запроса

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



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

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



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

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



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

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

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

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

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


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

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

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



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

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

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



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

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

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

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



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

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

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



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

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

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

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

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



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

Вывод


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

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

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

Автор: Вячеслав Москвин, Positive Technologies
Alt text
Комментарии для сайта Cackle