Содержание статьи
WARNING
Материал адpесован специалистам по безопасности и тем, кто собираeтся ими стать. Вся информация предоставлена исключительно в ознакомительных целях. Ни редакция, ни автор не несут ответственности за любой возможный вред, причиненный материалами данной статьи.
Тестовый стенд
Для проведения экспериментов нам понадобится тестовая площадка. Конечно, всегда можно самому установить и настроить нужный дистрибутив, но зачем, когда можно просто взять и сразу перейти непосредственно к опытам? Тем более наши китайские коллеги уже собрали готовую уязвимую среду и выложили ее в виде докер-контейнера. Скачать его можно из репозитория vulapps. На странице полно иероглифов, так что вот тебе команда, которая нужна для запуска сервера:
docker run --rm --cap-add=SYS_PTRACE --security-opt seccomp=unconfined -d -p 80:80 medicean/vulapps:n_nginx_1
Вообще, в этом репозитории ютится множество стендов для тестирования разных уязвимостей, советую изучить на досуге.
После успешного выполнения команды на 80-м порте у нас будет висеть подопытный веб-сервер nginx версии 1.13.1.
Если вдруг тебе захочется немного подебажить, то рекомендую приаттачиться к контейнеру и выполнить
apt-get update && apt-get install nano build-essential gdb nginx-dbg=1.13.1-1~stretch
service nginx stop && service nginx-debug start
ps -aux|grep nginx
Теперь можно присоединяться к процессу (worker process) с помощью gdb.
gdb --pid <pid>
Сразу предупреждаю, что импакт у уязвимости невесть какой, однако покопаться в ней интересно.
Немного о Range
Причина уязвимости — в некорректной обработке байтовых диапазонов в заголовке Range. Возможно, ты в курсе, что это такое, но давай на всякий случай повторим.
Заголовок Range используется, когда нужно получить не полный ответ от сервера, а только его часть. Формат допустимых значений заголовка подробно описан в спецификации протокола HTTP 1.1 RFC2616.
Согласно стандарту, в качестве значений можно указывать диапазоны для выборки. Они состоят из двух частей: размерность диапазона и список правил выборки. В качестве размерности используются байты.
Range: bytes=[-]<begin>-[<end>][,]
Саму выборку можно делать двумя способами. Первый — указать две позиции: начало выборки и ее конец. Причем стандарт четко определяет, что начальная позиция должна быть не меньше нуля, а конечная обязательно больше начальной или равна ей. Если это условие не соблюдается, то заголовок должен быть проигнорирован. Если в качестве последней позиции указано значение, которое больше размера запрашиваемого документа или равно ему, то последней позицией считается текущий размер документа в байтах минус один. То же самое касается тех случаев, когда конечная позиция не указана вообще.
К примеру, если размер документа 138 байт, то bytes=1-137
получит от сервера 137 байт информации, начиная со второго и заканчивая последним.
Помимо этого, в ответе присутствует заголовок Content-Range
, где после слеша указан полный размер документа, который мы запрашиваем.
Второй способ — выборка последних N байт тела документа. Если размер документа меньше, чем указанный в запросе, то будет выбран весь документ. Например, bytes=-7
запрашивает последние 7 байт.
Также спецификация разрешает в одном заголовке Range указать несколько диапазонов, в качестве разделителя используется запятая.
Отмечу, что если ответ сервера содержит заголовок Accept-Ranges
, то он поддерживает получение данных частями, когда в запросах есть хидер Range. Но, конечно же, это не говорит со стопроцентной вероятностью о его поддержке или ее отсутствии. Так что рекомендую все проверять на практике.
Углубляемся в детали
В nginx за обработку заголовка Range отвечает модуль ngx_http_range_header_filter_module
, который вызывает функцию ngx_http_range_header_filter
.
/src/http/modules/ngx_http_range_filter_module.c
146: static ngx_int_t
147: ngx_http_range_header_filter(ngx_http_request_t *r)
148: {
Если в пакете указан только один диапазон, то выводом информации занимается функция ngx_http_range_singlepart_header
, а если несколько, то ngx_http_range_multipart_header
.
Продолжение доступно только участникам
Вариант 1. Присоединись к сообществу «Xakep.ru», чтобы читать все материалы на сайте
Членство в сообществе в течение указанного срока откроет тебе доступ ко ВСЕМ материалам «Хакера», позволит скачивать выпуски в PDF, отключит рекламу на сайте и увеличит личную накопительную скидку! Подробнее
Вариант 2. Открой один материал
Заинтересовала статья, но нет возможности стать членом клуба «Xakep.ru»? Тогда этот вариант для тебя! Обрати внимание: этот способ подходит только для статей, опубликованных более двух месяцев назад.
Я уже участник «Xakep.ru»