Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
http2smugl — Обнаруживает и эксплуатирует уязвимости контрабанды HTTP-запросов при преобразовании HTTP/2 в HTTP/1.1, используя автоматизированные методы контрабанды заголовков для выявления несоответствий анализа бэкенда. | Kitploit
Инструменты/GitHubGitHub/neex/http2smugl
Анализ уязвимостейЭксплуатация веб-приложенийВеб-безопасностьТестирование на Проникновение
GitHubneex/http2smugl

http2smugl

Обнаруживает и эксплуатирует уязвимости контрабанды HTTP-запросов при преобразовании HTTP/2 в HTTP/1.1, используя автоматизированные методы контрабанды заголовков для выявления несоответствий анализа бэкенда.

Репозиторий
562741 год назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Поделиться

http2smugl

Этот инструмент помогает обнаруживать и эксплуатировать HTTP-контрабанду запросов (HTTP request smuggling) в случаях, когда она может быть достигнута через преобразование HTTP/2 -> HTTP/1.1 фронтальным сервером.

Схема выглядит следующим образом:

  1. Атакующий отправляет сфабрикованный HTTP/2-запрос на целевой сервер, который мы называем frontend (фронтальным).
  2. Запрос (предположительно) преобразуется в HTTP/1.1 и передается другому серверу — backend (бэкенду).

Атакующий хочет найти такой запрос, который бэкенд увидит как два отдельных запроса.

Если соединение frontend<->backend по HTTP/1.1 использует keep-alive, фронтальный сервер может отправлять запросы от других пользователей по тому же соединению. Если мы сможем «отравить» соединение частичным запросом, который идет после легитимного, мы сможем перехватить запрос другого пользователя.

Другие возможные сценарии включают обход защиты фронтального сервера и перезапись данных, отравление кэша (cache poisoning) или обман кэша (cache deception).

Для получения дополнительной информации об HTTP-контрабанде запросов обратитесь к Portswigger Web Security Academy.

Почему особое внимание HTTP/2?

В HTTP/2 имена и значения всех HTTP-заголовков являются бинарными. Это означает, что технически они могут содержать дополнительные пробелы или даже символы новой строки.

RFC7540#10.3 гласит, что реализации, преобразующие HTTP/2-запросы в HTTP/1, должны учитывать ограничения на набор символов, возникающие при таком преобразовании; большинство реализаций действительно их отклоняют. Несмотря на это, мы надеемся найти такие, которые допускают такие заголовки. Они исказят преобразованный HTTP/1.1-запрос, отправляемый бэкенду.

Другой момент: некоторые недавние исправления, связанные с HTTP-контрабандой запросов, могут быть реализованы только для парсеров HTTP/1.1.

В целом, мы надеемся, что существуют реализации HTTP/2, которые не очень осведомлены о последних исследованиях по HTTP-контрабанде в HTTP/1.1 и не включают соответствующих мер защиты.

Удалось ли найти хоть одну уязвимость с помощью этого?

Удивительно, но да!

Мне удалось обнаружить возможность протащить заголовок с пробелом через Cloudflare, открыв дверь для контрабанды Cloudflare<->клиент (в случае, если программное обеспечение клиента Cloudflare принимает и обрезает имена заголовков). Вот блог-пост.

Есть также еще один отчет по баг-баунти, который пока не опубликован. Он использует тот факт, что пользовательское ПО не отфильтровывает символы новой строки в заголовках HTTP/2, и контрабанда происходит на 100% (я вижу запросы других пользователей).

Однако я понимаю, что такие типы уязвимостей должны быть удручающе редки: в отличие от HTTP/1.1, реализаций HTTP/2 не так много, и большинство из них созданы с учетом безопасности, поэтому отклоняют подозрительные или недопустимые заголовки.

Алгоритм обнаружения

Инструмент имеет подкоманду, которая пытается автоматически определить, уязвима ли цель для атаки HTTP-контрабанды. Алгоритм, лежащий в основе этой функции, описан в данном разделе.

Для выполнения атаки HTTP-контрабанды нам фактически нужно сначала «протащить» один заголовок (либо Content-Length, либо Transfer-Encoding). Это означает, что нам нужно отправить заголовок, который а) контролирует, где заканчивается тело запроса, и б) не обрабатывается фронтальным сервером, но обрабатывается бэкендом.

Обычно это достигается путем модификации заголовка: добавление пробелов или табуляции в конце его имени, замена значения на полуэквивалентное и т.д.

Основная идея алгоритма обнаружения уязвимости — определить, обрабатывает ли сервер протащенный заголовок так, как если бы он был Content-Length или Transfer-Encoding. Это делается путем отправки нескольких запросов: одни с допустимыми, другие с недопустимыми значениями для этого заголовка. Затем мы пытаемся определить, есть ли способ отличить ответы этих двух групп.

Вот почему в выводе инструмента нет слов «уязвим/неуязвим»: он просто сообщает, может ли он различить ответы, пришедшие на запросы из этих двух групп.

Инструмент считает два набора HTTP-ответов различимыми, если выполняется хотя бы одно из следующих двух условий:

  1. Множества кодов ответов не пересекаются.
  2. Множества длин ответов отделимы друг от друга (например, все «допустимые» ответы длиннее 1000 байт, а все «недопустимые» — короче).

Таймауты обрабатываются как уникальное значение кода состояния, не равное никакому другому; таким образом, инструмент превосходит классическую схему «обнаружение по времени».

Рассмотрим пример. Предположим, мы пытаемся протащить заголовок transfer-encoding, заменив дефис на подчеркивание.

Если окажется, что сервер отвечает статусом 400 каждый раз, когда мы отправляем transfer_encoding:zalupa, и зависает при transfer_encoding:chunked, мы можем сказать, что сервер, вероятно, обрабатывает этот заголовок как значение для transfer encoding. Теоретически это может быть либо фронтальный сервер, либо бэкенд.

Первый случай неинтересен, так как мы в любом случае могли бы отправить непротащенную версию заголовка, а второй — это то, что мы ищем. Поскольку все происходит через HTTP/2, первого случая в большинстве случаев можно избежать: HTTP/2-сервер определяет конец тела запроса другим способом, не связанным с заголовками запроса, и никогда не ожидает, что тело будет в формате chunked HTTP/1.1 (с шестнадцатеричными длинами фрагментов).

Конкретные вариации методов обнаружения:

  1. Мы отправляем протащенную версию Transfer-Encoding: chunked (например, transfer_encoding:chunked) и разные тела: допустимое — 0\r\n\r\n, недопустимое — 999\r\n.

    В случае, если ответы различаются, мы можем быть уверены, что бэкенд получает и обрабатывает протащенный заголовок. У фронтального сервера нет причин делать это: HTTP/2 не использует отправленный нами формат chunked, поэтому он был бы недопустим.

    Мы ожидаем, что бэкенд зависнет (то есть запрос приведет к таймауту) для недопустимых запросов, так как он ждет поступления дополнительных данных.

    Это самый надежный вариант обнаружения: если сервер зависает при чтении тела, что-то, вероятно, пошло не так, поскольку в HTTP/2-запросе нет использования передачи с разбиением на фрагменты HTTP/1.1.

  2. Мы снова отправляем протащенную версию Transfer-Encoding и разные тела: 0\r\n\r\n как допустимое тело и X\r\n\r\n как недопустимое.

    Случай тот же, что и выше, но вместо чтения тела мы ожидаем, что бэкенд хотя бы проверит его.

  3. Мы отправляем протащенную версию заголовка Content-Length со значениями 1 и -1.

Отправляя несколько пар допустимых/недопустимых запросов, мы можем снизить вероятность случайных ложных срабатываний. С другой стороны, мы можем остановиться раньше, если увидим, что нет никакой возможности разделить ответы на «допустимые» и «недопустимые» запросы.

Техники контрабанды

Инструмент пытается применить несколько техник контрабанды, модифицируя заголовок различными способами. Ни одна из них не является новой и одновременно неочевидной.

Пробелы

Чтобы протащить заголовок, мы добавляем к нему пробел. Это самый распространенный и классический метод. Мы надеемся, что заголовок не обрабатывается фронтальным сервером, но отправляется как есть бэкенду, который удаляет пробел.

Инструмент пробует различные символы в качестве пробелов: включает , \t, \v, \x00 и символы Юникода.

Подчеркивание

Чтобы протащить заголовок, мы заменяем дефис (-) на подчеркивание (_). Если бэкенд каким-то образом основан на CGI, он может преобразовывать заголовки вида Header-Name в форму HEADER_NAME; таким образом, дефис все равно станет подчеркиванием. При определении способа парсинга тела такой бэкенд, предположительно, запрашивает значение CONTENT_LENGTH / TRANSFER_ENCODING из своего словаря заголовков, и оно там будет.

Символы новой строки

Это специфично для HTTP/2. Поскольку HTTP/2 — бинарный протокол, мы можем попытаться отправить символы новой строки в имени или значении заголовка. Стандарт это запрещает, но мы надеемся найти реализацию, которая все еще это принимает.

При преобразовании HTTP/2 -> HTTP/1.1 заголовок разделяется на два разных заголовка, то есть запрос будет выглядеть для бэкенда иначе.

Чтобы протащить заголовок, мы помещаем его имя и значение после символа новой строки: заголовок с именем "Transfer-Encoding" и значением "chunked" превращается в заголовок с именем "fake" и значением "fake\r\ntransfer-encoding: chunked".

UTF-символы

Предположим, что бэкенд использует какой-то высокоуровневый язык и не выполняет достаточной проверки заголовков. В этом случае он может преобразовывать имена в верхний регистр перед любыми другими действиями и делать это с использованием функций, учитывающих Юникод. К счастью, слово TRANSFER-ENCODING содержит букву S, которая является верхним регистром ſ (\u017f).

Аналогично, мы можем поискать бэкенд, который будет преобразовывать значение Transfer-Encoding в нижний регистр: мы отправляем chunKed вместо chunked с \u212a вместо K.

Конечно, требуется, чтобы фронтальный сервер передавал имена/значения заголовков в UTF-8 на бэкенд.

Использование

Чтобы установить инструмент, выполните go install github.com/neex/http2smugl@latest.

Инструмент содержит две подкоманды: request и detect. Первая предназначена только для формирования HTTP/2-запросов: большинство клиентских инструментов не принимают недопустимые заголовки, поэтому удобно иметь такой, который отправляет пользовательский ввод на сервер как есть.

Вторая — detect. Она пробует различные техники HTTP-контрабанды, чтобы определить, уязвима ли цель. Алгоритм обнаружения сложен; я буду признателен, если вы прочтете его и пришлете мне свои комментарии. Он описан ниже в соответствующем разделе.

http2smugl request

Используйте эту подкоманду для отправки (возможно, слегка искаженного) HTTP/2-запроса. Первый параметр — URL, остальные — заголовки в формате name:value (обратите внимание: после двоеточия нет пробела). Поддерживается экранирование обратной косой чертой: можно использовать escape-последовательности \r, \n и \xXX. Например, чтобы отправить имя заголовка, содержащее двоеточие, используйте \x3a (например, name\x3awith\x3acolons:value).

http2smugl detect

Эта подкоманда пытается обнаружить HTTP-контрабанду с помощью различных техник. Для использования просто выполните http2smugl detect [HTTPS URL].

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

Поддержка HTTP/3

Реализована экспериментальная поддержка HTTP/3 (quic). Однако я не рекомендую использовать её, поскольку я не нашел ни одной ошибки, связанной с HTTP/3.

Чтобы использовать HTTP/3 с подкомандой request, укажите протокол https+h3:// в URL вместо просто https. То же самое поддерживается в команде detect.

Также существует флаг --try-http3 для подкоманды request, который меняет поведение, если протокол не указан в URL (только имя хоста). Если флаг присутствует, команда будет пробовать протокол https+h3 для таких записей в командной строке или файле целей (а также HTTP/2). Например, http2smugl detect --try-http3 www.example.com будет пробовать и HTTP/3, и HTTP/2, но http2smugl detect --try-http3 https://www.example.com/ будет пробовать только HTTP/2.

Известные ложные срабатывания

В этом разделе я описываю некоторые случаи, когда инструмент сообщает, что ответы «различимы», но уязвимости не может существовать.

ELB

Amazon Elastic Load Balancer реализует множество защитных мер от HTTP-контрабанды. Хотя не все из них отклоняют запрос по умолчанию, ELB не будет повторно использовать соединение после отправки запроса с «подозрительными» заголовками. Таким образом, реальная атака невозможна.

Чтобы определить, что вы имеете дело с ELB, можно воспользоваться заголовком ответа Server. Если он скрыт, вы можете отправить запрос с заголовком content__length (обратите внимание на двойное подчеркивание) и значением -1: если в ответ приходит 400, вероятно, это ELB или другой WAF (см. ниже).

Apache Traffic Server

Apache Traffic Server в основном используется в Yahoo. Он обрабатывает HTTP/2 необычным образом: преобразует его в HTTP/1.1 в памяти, а затем повторно анализирует полученный запрос. Таким образом, хотя технически заголовок может быть «протащен», это не приведет к уязвимости: вы могли бы отправить те же байты в HTTP/1.1-соединение.

Самый простой способ определить, что вы имеете дело с ATS (кроме заголовка Server), — отправить запрос TRACE. Если в запросе присутствует заголовок Max-Forwards: 0, ATS по умолчанию вернет ответ на запросы TRACE, не пересылая их на бэкенд.

Microsoft IIS

Похоже, что Microsoft IIS поддерживает декодирование chunked-кодирования внутри тел HTTP/2. Это странное поведение; однако с точки зрения безопасности оно безобидно.

Чтобы определить, что вы имеете дело с IIS, можно отправить запрос с заголовком "transfer-encoding:chunked" и некорректным chunked-телом. Если в заголовке Server вы видите Microsoft-HTTPAPI или что-то подобное, значит, это он.

Другие WAF

WAF пытаются обнаружить подозрительные заголовки — это их работа. Иногда это приводит к тому, что инструмент говорит «различимо»: WAF может заблокировать что-то вроде content_length:-1 и пропустить content_length:1 просто потому, что его фильтры принимают такие решения.

Если вы видите WAF в заголовке Server, вероятно, это ложное срабатывание.

Контакты

Если у вас есть какие-либо идеи по этой теме, свяжитесь со мной через @emil_lerner в Twitter или @neexemil в Telegram — или создайте issue здесь на GitHub.

Скачать инструмент

Оба значения недопустимы с точки зрения фронтального сервера: в HTTP/2 существует другой механизм определения длины тела, и в обоих случаях тело запроса фактически не отправляется. Если ответы различаются, мы предполагаем, что заголовки анализировал именно бэкенд.

Этот метод наименее надежен: фронтальный сервер может выдавать разные ошибки, когда Content-Length имеет недопустимое значение и когда оно не соответствует фактической длине.