Размер шрифта:
Эксплуатируй, GoAhead! Выполняем произвольный код в веб-сервере GoAhead
Play

Эксплуатируй, 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.

📎📎📎📎📎📎📎📎📎📎