Продвинутая контрабанда запросов
В этом разделе вы расширите изученные концепции и научитесь более продвинутым техникам контрабанды HTTP-запросов. Мы также рассмотрим разнообразные атаки на базе HTTP/2, ставшие возможными благодаря уникальным возможностям Burp по тестированию HTTP/2. Не переживайте, если вы новичок в HTTP/2 — по ходу дела мы разберём все основы.
В частности, мы рассмотрим:
Как распространённые реализации HTTP/2 открывают ряд новых мощных векторов, делая уязвимыми многие сайты, ранее защищённые от подобных атак.
Как с помощью контрабанды запросов можно персистентно отравить очередь ответов, фактически добиваясь захвата всего сайта.
Как использовать эксклюзивные для HTTP/2 входные данные, чтобы строить эксплойты высокой критичности даже если цель вообще не использует повторно соединение между фронтендом и бэкэндом.
Чтобы вы могли отработать изученное на практике, по ходу материала вы найдете намеренно уязвимые лабораторные задания. Они основаны на реальных уязвимостях, впервые представленных на Black Hat USA 2021 Джеймсом Кеттлом.
Контрабанда запросов в HTTP/2
В этом разделе вы узнаете, как вопреки распространённому мнению внедрение HTTP/2 фактически сделало многие сайты более уязвимыми к контрабанде запросов, даже если раньше они были защищены от подобных атак.
Длина сообщения в HTTP/2
Контрабанда запросов по сути эксплуатирует расхождения в том, как разные серверы интерпретируют длину запроса. HTTP/2 вводит единый надёжный механизм для этого, который долгое время считался делающим протокол по своей природе невосприимчивым к контрабанде.
Хотя вы не увидите этого в Burp, сообщения HTTP/2 «по проводу» отправляются как серия отдельных фреймов. Каждый фрейм предваряется явным полем длины, которое сообщает серверу, сколько байтов нужно прочитать. Следовательно, длина запроса — это сумма длин его фреймов.
В теории этот механизм означает, что у атакующего нет возможности внести необходимую неоднозначность для контрабанды запросов, если сайт использует HTTP/2 сквозным образом. На практике же часто бывает иначе из-за широко распространённой, но опасной практики понижения версии HTTP/2.
Понижение версии HTTP/2 (HTTP/2 downgrading)
Понижение версии HTTP/2 — это процесс переписывания запросов HTTP/2 в синтаксис HTTP/1 для формирования эквивалентного HTTP/1-запроса. Веб-серверы и обратные прокси часто делают это, чтобы предлагать поддержку HTTP/2 клиентам, одновременно общаясь с бэкэнд-серверами, которые говорят только на HTTP/1. Эта практика необходима для многих атак, рассмотренных в этом разделе.

Уязвимости H2.CL
Запросы HTTP/2 не обязаны явно указывать свою длину в заголовке. При понижении версии это означает, что фронтенд-серверы часто добавляют заголовок HTTP/1 Content-Length, вычисляя его значение с использованием встроенного в HTTP/2 механизма определения длины. Интересно, что запросы HTTP/2 также могут включать собственный заголовок content-length. В этом случае некоторые фронтенд-серверы просто переиспользуют это значение в результирующем HTTP/1 запросе.
Спецификация предписывает, чтобы любой заголовок content-length в запросе HTTP/2 соответствовал длине, рассчитанной встроенным механизмом, но это не всегда корректно валидируется до понижения версии. В итоге может оказаться возможным провозить запросы путём внедрения вводящего в заблуждение заголовка content-length. Хотя фронтенд будет использовать неявную длину HTTP/2, чтобы определить конец запроса, бэкэнд HTTP/1 вынужден опираться на заголовок Content-Length, полученный из вашего внедрённого значения, что приводит к десинхронизации.
Front-end (HTTP/2)
:method
POST
:path
/example
:authority
vulnerable-website.com
content-type
application/x-www-form-urlencoded
content-length
0
Back-end (HTTP/1)
Уязвимости H2.TE
Блочное кодирование (chunked transfer encoding) несовместимо с HTTP/2, и спецификация рекомендует удалять любой заголовок transfer-encoding: chunked, который вы попытаетесь внедрить, либо блокировать запрос целиком. Если фронтенд-сервер этого не делает и затем понижает запрос для бэкэнда HTTP/1, который поддерживает блочное кодирование, это также может привести к атаке контрабанды.
Front-end (HTTP/2)
:method
POST
:path
/example
:authority
vulnerable-website.com
content-type
application/x-www-form-urlencoded
transfer-encoding
chunked
Back-end (HTTP/1)
Если сайт уязвим к контрабанде запросов H2.CL или H2.TE, вы потенциально можете использовать это поведение для проведения тех же атак, что мы разбирали в в предыдущих материалах.
Скрытая поддержка HTTP/2
Браузеры и другие клиенты, включая Burp, обычно используют HTTP/2 только при общении с серверами, которые явно объявляют поддержку через ALPN как часть TLS-рукопожатия. Некоторые серверы поддерживают HTTP/2, но из-за неправильной конфигурации не заявляют об этом должным образом. В таких случаях может показаться, что сервер поддерживает только HTTP/1.1, поскольку клиенты по умолчанию откатываются к нему. В результате тестировщики могут упустить доступную поверхность атаки HTTP/2 и пропустить проблемы на уровне протокола, как было в примерах, которые мы рассмотрели выше.
Чтобы настроить Burp Repeater использовать HTTP/2 и вручную протестировать такую неправильную конфигурацию:
Откройте Settings и перейдите в Tools > Repeater.
В разделе Connections включите опцию Allow HTTP/2 ALPN override.
В Repeater откройте панель Inspector и раскройте раздел Request attributes.
Переключателем установите Protocol в HTTP/2. Теперь Burp будет отправлять все запросы на этой вкладке по HTTP/2, независимо от того, объявляет ли сервер поддержку.
Отравление очереди ответов
Response queue poisoning — мощная атака контрабанды запросов, позволяющая красть произвольные ответы, предназначенные другим пользователям, потенциально компрометируя их аккаунты и даже весь сайт.
Контрабанда запросов через внедрение CRLF
Даже если сайты принимают меры против базовых атак H2.CL или H2.TE, например валидируют content-length или удаляют любые заголовки transfer-encoding, бинарный формат HTTP/2 открывает новые способы обхода таких мер защиты.
В HTTP/1 иногда можно эксплуатировать расхождения в обработке одиночных символов перевода строки (\n) серверами, чтобы провезти запрещённые заголовки. Если бэкэнд считает это разделителем, а фронтенд — нет, некоторые фронтенды вовсе не заметят второй заголовок.
Такого расхождения нет при обработке полной последовательности CRLF (\r\n), потому что все серверы HTTP/1 единодушны, что это завершает заголовок.
С другой стороны, поскольку сообщения HTTP/2 бинарные, а не текстовые, границы каждого заголовка базируются на явных заранее заданных смещениях, а не на символах-разделителях. Это значит, что \r\n больше не имеет особого значения внутри значения заголовка и, следовательно, может быть включён внутрь значения без разделения заголовка:
foo
bar\r\nTransfer-Encoding: chunked
На первый взгляд это безобидно, но при переписывании в запрос HTTP/1 \r\n снова будет интерпретирован как разделитель заголовков. В результате бэкэнд-сервер HTTP/1 увидит два отдельных заголовка:
Разделение запросов в HTTP/2
Когда мы рассматривали отравление очереди ответов, вы узнали, как разделить один HTTP-запрос на ровно два полных запроса на бэкэнде. В том примере разделение происходило внутри тела сообщения, но при понижении HTTP/2 вы можете вызвать разделение и в заголовках.
Этот подход более универсален, потому что вы не зависите от использования методов, которым позволено тело запроса. Например, вы можете даже использовать GET:
:method
GET
:path
/
:authority
vulnerable-website.com
foo
Это также полезно в случаях, когда content-length валидируется, а бэкэнд не поддерживает блочное кодирование.
Учёт переписывания на фронтенде
Чтобы разделить запрос в заголовках, нужно понимать, как фронтенд-сервер переписывает запрос, и учитывать это при ручном добавлении любых заголовков HTTP/1. Иначе один из запросов может оказаться без обязательных заголовков.
Например, необходимо убедиться, что оба запроса, получаемые бэкэндом, содержат заголовок Host. Фронтенд-серверы обычно удаляют псевдозаголовок :authority и заменяют его новым заголовком HTTP/1 Host при понижении. Существуют разные подходы к этому, и от них зависит, куда нужно поместить внедряемый вами заголовок Host. Рассмотрим следующий запрос:
:method
GET
:path
/
:authority
vulnerable-website.com
foo
При переписывании некоторые фронтенды добавляют новый Host в конец текущего списка заголовков. С точки зрения HTTP/2 фронтенда это после заголовка foo. Обратите внимание, что это также после точки, в которой на бэкэнде произойдёт разделение. Значит, первый запрос останется вовсе без Host, а контрабандный запрос получит два. В этом случае нужно расположить внедряемый Host так, чтобы он оказался в первом запросе после разделения:
:method
GET
:path
/
:authority
vulnerable-website.com
foo
Аналогично потребуется отрегулировать расположение любых внутренних заголовков, которые вы хотите внедрить.
Туннелирование HTTP-запросов
Многие атаки контрабанды, рассмотренные ранее, возможны лишь потому, что одно и то же соединение между фронтендом и бэкэндом используется для обработки нескольких запросов. HTTP request tunnelling позволяет разработать эксплойты высокой критичности даже при полном отсутствии переиспользования соединений.
Контрабанда 0.CL
Атаки десинхронизации 0.CL возникают, когда фронтенд игнорирует заголовок Content-Length, который бэкэнд обрабатывает. Долгое время считалось, что такой сценарий неэксплуатируем из-за взаимной блокировки соединений между серверами.
Однако, комбинируя атаку 0.CL с гаджетом раннего ответа (early-response gadget) — техникой, заставляющей бэкэнд отвечать до получения полного тела запроса — атакующие могут снять взаимную блокировку, а затем с помощью двойной десинхронизации построить полноценный эксплойт. Этот прорыв делает возможной эксплуатацию сценариев 0.CL.
За техническими подробностями см. сопроводительный доклад: HTTP/1.1 Must Die.
Last updated