Эксплуатируй, GoAhead! Выполняем произвольный код в веб-сервере GoAhead
Баг обнаружил австралийский исследователь Дэниел Ходсон (Daniel Hodson) из компании Elttam. Причина уязвимости — в некорректной обработке HTTP-запросов при включенной поддержке интерфейса CGI. При инициализации окружения некоторых CGI-скриптов существует возможность использования специальных переменных окружения типа LD_PRELOAD , с помощью которых можно выполнить код.
Уязвимость получила идентификатор CVE-2017-17562.
Готовим стенд
Для начала определимся с версией самого веб-сервера. Уязвимы все версии приложения ниже 3.6.5. Так что будем использовать самую последнюю до патча — 3.6.4. В качестве системы я буду использовать Debian 9 в докер-контейнере.
Теперь ставим веб-сервер. Будем собирать из исходников, взятых в гит-репозитории.
Теперь скомпилим тестовое CGI-приложение.
Запустить сервер можно из папки test, там лежат дефолтные конфиги для работы GoAhead.
Скомпилированное приложение будет доступно на запущенном сервере по ссылке /cgi-bin/cgitest .
Тестовое CGI-приложение на сервере GoAhead 6.3.4
Если тебе захочется вместе со мной заглянуть в исходники веб-сервера, то их ты всегда сможешь найти в официальном репозитории Embedthis.
Особенности компиляции и линкования
Для начала посмотрим, как устроен бинарник goahead . Для этого можно воспользоваться утилитой readelf или file .
Детальная информация о бинарнике goahead
Обрати внимание на секцию INTERP . Она говорит нам о том, что ELF-файл был скомпилирован с динамической линковкой библиотек, а в качестве интерпретатора для ее выполнения используется библиотека ld-linux-x86-64.so.2 .
Эта библиотека — динамический компоновщик, который выполняется в первую очередь при запуске исполняемого файла и отвечает за линковку и загрузку разделяемых библиотек (so) и символов. Чтобы узнать, какие библиотеки подгружаются при старте веб-сервера, можно воспользоваться утилитой ldd или запустить goahead , установив переменную окружения LD_TRACE_LOADED_OBJECTS в 1 .
Информация о подгружаемых библиотеках при запуске веб-сервера GoAhead
Как ты, наверное, знаешь, переменных окружения великое множество, но среди них есть специальные, которые могут изменить дефолтное поведение ступеней загрузки исполняемого файла. Подробнее о них ты можешь прочитать в мануале к файлу ld.so .
Давай пробежимся по исходникам и посмотрим, каким образом переменные влияют на работу приложения. Для этого заглянем в библиотеку glibc, исходники которой ты можешь найти в этом репозитории.
Первая остановка — это функция dl_main , которая выполняется сразу после запуска линкера.
/glibc/master/elf/rtld.cЗдесь вызывается функция process_envvars . Да, она делает то, что и следует из ее названия, — парсит и обрабатывает переменные окружения.
/glibc/master/elf/rtld.cПеребирается массив, состоящий из переменных окружения. Для каждого элемента вычисляется его размер, а дальше функция switch, в зависимости от длины параметра, передает управление на различные ветки кода, где выясняется, не передана ли одна из специальных переменных. Нас больше всего интересует длина 7 , где расположилась проверка на LD_PRELOAD в переданной командной строке.
/glibc/master/elf/rtld.cЕсли эта переменная окружения передана, то инициализируется preloadlist . По сути, в ней хранится список библиотек, которые нужно загрузить перед тем, как передавать управление главному коду из бинарника. Поэтому вскоре после того, как отработала process_envvars , выполнение переходит к той части кода, что начинает обработку preloadlist .
/glibc/master/elf/rtld.cЕсли список непустой, то функция do_preload выполняет загрузку каждой переданной библиотеки.
/glibc/master/elf/rtld.cТеперь мы знаем, что, если указать в переменной LD_PRELOAD путь до библиотеки, линкер выполнит ее загрузку перед исполнением самого бинарника. Однако нам этого недостаточно, ведь задача не в том, чтобы просто подцепить либу, требуется еще и выполнить нужный нам код. В этом нам помогут секции .init и .fini . Если функция помещена в секцию .init , то система выполняет ее до главной (main), а если в .fini , то после того, как main отработает и вернет результат. Своеобразная имплементация конструкторов и деструкторов классов, только глобальная.
Теперь воспользуемся атрибутами функций. Нужный нам называется constructor .
Продолжение доступно только участникам Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайтеЧленство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материалЗаинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Специалист по информационной безопасности в ONsec. Research, ethical hacking and Photoshop.