РАЗБОР
Статья
HTTP Request Smuggling эксплуатация: методология 2026
[ обложка статьи ]
Режим чтения
На пентесте финтех-API за Cloudflare в начале 2025-го я два дня гонял стандартный OWASP-чеклист - ноль findings. WAF блокировал SQLi, XSS, SSRF. На третий день запустил HTTP Request Smuggler в Burp, поймал timeout-аномалию на POST-запросе с двойными CL/TE заголовками - и через четыре часа у меня был рабочий CL.TE PoC с кражей сессионных cookie. Баг ушёл на Critical. Ни один WAF не спасёт, если front-end и back-end по-разному считают байты в теле запроса.
Бизнес-логика desync атаки: зачем это атакующему
HTTP Request Smuggling (CWE-444 - Inconsistent Interpretation of HTTP Requests) не эксплуатирует баг в коде приложения. Это инфраструктурная уязвимость - front-end back-end рассинхронизация в парсинге границ HTTP-сообщений. Reverse proxy видит один запрос, back-end - два. Остаток первого запроса «приклеивается» к следующему в TCP-потоке. Подробнее - в нашем подробном разборе пентест веб-приложений.Что это даёт атакующему в терминах MITRE ATT&CK:
| Цель | Техника ATT&CK | Механизм через HRS |
|---|---|---|
| Проникновение через WAF | Exploit Public-Facing Application (T1190, Initial Access) | Smuggled-запрос обходит все проверки front-end |
| Отравление кэша | Content Injection (T1659, Initial Access) | Подмена ответа в кэше CDN/Varnish для всех пользователей |
| Перехват сессий | Steal Web Session Cookie (T1539, Credential Access) | "Поглощение" Cookie-заголовка из чужого запроса |
Монетизация прямолинейна: один smuggled
GET /admin - ACL перестаёт существовать. Один отравленный кэш - тысячи пользователей получают XSS-payload. Bug bounty выплаты за CWE-444 стабильно высокие, потому что импакт почти всегда Critical. Тема не нова - первая публикация датируется 2005 годом, но второе дыхание открыли работы James Kettle из PortSwigger: "HTTP desync attacks" (2019), "Browser-Powered Desync Attacks" (2022), "HTTP/1.1 must die: the desync endgame" (2025).Fingerprinting стека: первый шаг методологии пентеста HTTP Request Smuggling
Прежде чем слать smuggling-payload, нужно понять архитектуру цепочки. Без fingerprinting - стрельба вслепую: payload для CL.TE не сработает на TE.CL-стеке.
Типичные уязвимые связки
HRS работает только при наличии минимум двух компонентов с разным парсером HTTP. Вот связки, на которых я находил desync в реальных проектах:| Front-end | Back-end | Вероятный вариант | Почему |
|---|---|---|---|
| Nginx (proxy_request_buffering off) | Gunicorn / uWSGI | CL.TE, CL.0 | Nginx передаёт тело без пересборки |
| HAProxy | Apache / PHP-FPM | TE.CL, CL.TE | Разница в приоритете CL vs TE |
| CDN (Cloudflare, AWS ALB) | Любой HTTP/1.1 back-end | H2.CL, H2.TE | HTTP/2 downgrade при конвертации |
| Varnish | Node.js / Express | CL.TE | Varnish не всегда валидирует TE |
Методы определения
Заголовки ответа. ПростойGET / покажет Server, Via, X-Powered-By, X-Cache. Заголовок Via явно указывает промежуточные прокси. Cloudflare добавляет cf-ray, AWS ALB - x-amzn-requestid, HAProxy может выдать X-Haproxy-*. Комбинация Server: nginx в ответе и X-Powered-By: Express - классический маркер двухзвенной цепочки.HTTP/2 detection. Команда
curl --http2 -v https://target.com 2>&1 | grep "ALPN" покажет, принимает ли front-end HTTP/2. Если да, а back-end отвечает по HTTP/1.1 (видно по внутренним ошибкам или по заголовкам) - перед вами сценарий HTTP/2 downgrade smuggling. Самый актуальный вектор 2026 года, и большинство пентестеров его до сих пор пропускают.Timing-проба. Отправьте запрос с
Transfer-Encoding: chunked и намеренно разорванным чанком (без терминирующего 0\r\n\r\n). Задержка 5+ секунд - back-end ждёт завершения chunked-данных и использует TE. Мгновенный ответ - back-end игнорирует TE и опирается на CL. Эта разница - первый индикатор потенциальной desync атаки.Детектирование desync: timing-based workflow
X), поскольку оно уже буферизовано в TCP-потоке. Back-end использует Content-Length: 6, ожидает 6 байт тела. Фактически полученные данные 0\r\n\r\nX - это 5 байт, back-end ждёт ещё один. Timeout. Задержка подтверждает TE.CL. Механика подробно описана в PortSwigger Web Security Academy (TE.CL vulnerabilities).Differential response как альтернатива
Когда timing не показателен (back-end быстро дропает malformed-запросы), работает differential response. Smuggled-запрос содержитGET /obviously-nonexistent-path HTTP/1.1. Если следующий легитимный запрос через то же keep-alive соединение возвращает 404 вместо ожидаемого 200 - desync подтверждён. Метод реализован в расширении HTTP Request Smuggler.Критический момент: в Burp Repeater нужно отключить автоматический пересчёт Content-Length (Repeater -> Uncheck "Update Content-Length"). Без этого Burp "починит" ваш payload, и timing-проба покажет ложноотрицательный результат. Также убедитесь, что запрос уходит по HTTP/1.1, а не HTTP/2 - переключается в Inspector -> Request attributes.
Варианты эксплуатации desync уязвимостей
CL.TE и TE.CL: классика смаглинга запросов
Механика CL.TE уязвимости: front-end читает тело по Content-Length (включая smuggled-запрос), передаёт целиком. Back-end парсит chunked, видит нулевой чанк - конец первого запроса. Всё после нулевого чанка интерпретируется как начало нового запроса.Для TE.CL - зеркально. Front-end парсит chunked до нулевого чанка и передаёт. Back-end читает по Content-Length, обрезает тело раньше - остаток становится следующим запросом.
По данным PortSwigger Web Security Academy, CL.TE - самый распространённый вариант в production. На моей практике подтверждается: из десяти найденных HRS за последний год семь были CL.TE, два - TE.CL, один - H2.CL.
TE.TE рассинхронизация через обфускацию Transfer-Encoding
Когда оба сервера поддерживают TE, нужно заставить один из них проигнорировать заголовок. Рабочие варианты обфускации:
Код:
Transfer-Encoding: chunkedx
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:
chunked
Transfer-Encoding: chunked
Transfer-encoding: identity
HTTP/2 downgrade smuggling: H2.CL и H2.TE
Ключевой вектор HRS атаки на веб-приложения в 2026 году. HTTP/2 сам по себе иммунен к классическому smuggling - длина сообщения определяется бинарными фреймами, а не текстовыми заголовками. Но end-to-end HTTP/2 - редкость. Обычно CDN или балансировщик принимает HTTP/2 и конвертирует в HTTP/1.1 для back-end (HTTP downgrading).H2.CL: атакующий отправляет HTTP/2 запрос с явным заголовком
content-length, конфликтующим с фактической длиной данных во фрейме. Front-end при конвертации в HTTP/1.1 сохраняет этот Content-Length, а back-end рассчитывает длину по реально полученным данным. Разница - классический primitive для smuggling.H2.TE: атакующий внедряет
transfer-encoding: chunked в HTTP/2 запрос. Спецификация HTTP/2 запрещает TE кроме trailers, но некоторые front-end при downgrade передают заголовок на back-end как есть. Back-end начинает парсить chunked - и дальше цепочка эксплуатации аналогична HTTP/1.1.Для тестирования H2 вариантов в Burp переключите протокол в Inspector -> Request attributes -> HTTP/2. В
curl используйте --http2 с --http1.1 на разных этапах для сравнения поведения.CL.0 и 0.CL: обход прокси и балансировщиков HTTP без конфликта заголовков
CL.0 - back-end полностью игнорирует Content-Length и не читает тело запроса для определённых endpoint'ов или методов. Всё, что передано в теле, автоматически становится началом следующего запроса. Конфликт CL/TE не нужен - достаточно найти endpoint, где back-end не обрабатывает body.0.CL - front-end игнорирует Content-Length и не пересылает тело. Back-end ожидает данные, которые не придут. Если по тому же keep-alive соединению приходит запрос другого пользователя, back-end может интерпретировать его как тело предыдущего запроса.
Оба варианта описаны в исследовании YesWeHack и встречаются реже классических CL.TE/TE.CL, но опасны именно тем, что не детектируются стандартными проверками на конфликт CL и TE заголовков. Сканер пройдёт мимо, а десинк - вот он.
Инструменты для веб пентеста HRS
HTTP Request Smuggler (Burp-расширение от PortSwigger) - основной инструмент. Запускается через контекстное меню на запросе (Extensions -> HTTP Request Smuggler -> Smuggle probe). Перебирает CL.TE, TE.CL, TE.TE с десятками обфускаций, результаты - в Dashboard с severity и confidence. Начинайте с него.Turbo Intruder - когда нужен точный контроль над timing. Позволяет слать запросы с заданной частотой и мерить response time с миллисекундной точностью. Незаменим для edge-кейсов, где стандартный таймаут HTTP Request Smuggler не срабатывает.
smuggler.py (defparam/smuggler) - CLI-инструмент на Python 3. Запуск:
python3 smuggler.py -u https://target.com. Тестирует набор payload'ов для CL.TE/TE.CL и выводит результат с цветовой индикацией. Удобен для массового сканирования на этапе recon.h2csmuggler (Python, BishopFox) - заточен под HTTP/2 cleartext (h2c) smuggling сценарии. Запуск:
python3 h2csmuggler.py -x https://target.com. Позволяет тестировать обход reverse proxy через h2c upgrade.Raw TCP через Python. Когда Burp нормализует "грязные" заголовки (удаляет пробелы, фиксит регистр), я переключаюсь на raw sockets. Библиотека
h11 позволяет конструировать HTTP/1.1-сообщения побайтово, hyper - работать с HTTP/2 фреймами. Для TE.TE обфускации с obs-fold или нестандартными символами это единственный надёжный способ. Burp тут бессилен - он слишком "вежливый" для таких грязных трюков.От primitive к impact: цепочки эксплуатации
Desync сам по себе - primitive, не импакт. Для bug bounty отчёта нужна цепочка до конкретного последствия.Обход ACL (Broken Access Control, A01:2021)
Smuggled-запрос содержитGET /admin HTTP/1.1 с Host: target.com. Front-end никогда не пропустил бы - ACL блокирует /admin для внешних IP. Но smuggled-запрос проходит мимо front-end и попадает на back-end напрямую. Back-end не проверяет ACL - он доверяет front-end. Результат: доступ к административной панели, удаление пользователей, чтение конфигурации. ACL как будто и нет.Cache Poisoning (CPDoS)
Если перед back-end стоит кэширующий слой (Varnish, CDN), smuggled-запрос отравляет кэш. SmuggledGET / с Host: evil.com заставляет back-end вернуть 301 Redirect на evil.com. Кэш сохраняет этот ответ для URI /. Все последующие пользователи получают редирект. Это Content Injection (T1659) - массовое воздействие через один запрос. Исследователи демонстрировали подобные атаки на инфраструктуре крупных технологических компаний через CL.TE уязвимости в рамках bug bounty программ.Кража сессий (T1539)
Smuggled POST-запрос направлен на endpoint, сохраняющий данные (комментарий, профиль, feedback-форма). Тело smuggled-запроса обрезано - back-end ждёт продолжения. Следующий запрос другого пользователя (включая егоCookie-заголовок) "вклеивается" в тело первого. Атакующий читает свой комментарий - и видит чужую сессию.Работает только на стеках с keep-alive между front-end и back-end и общим пулом соединений. Но когда работает - это самый красивый (и самый пугающий для заказчика) PoC.
Предусловия и ограничения
| Фактор | Требование | Что проверять |
|---|---|---|
| Keep-alive | Соединения между front-end и back-end переиспользуются | Заголовок Connection в ответе |
| Буферизация | proxy_request_buffering off в Nginx расширяет поверхность | Конфиг Nginx или timing-поведение |
| HTTP/2 front-end | Front-end принимает H2 и конвертирует в H1.1 | ALPN negotiation через curl --http2 -v |
| Общий пул | Запросы разных пользователей идут через одно back-end соединение | Timing-корреляция между запросами |
| Версия ПО | Конкретные версии прокси/серверов с известными quirks | Server заголовок, fingerprinting |
Nginx с дефолтной конфигурацией (
proxy_request_buffering on) пересобирает запрос с явным Content-Length перед отправкой на back-end. Это разрушает большинство CL.TE/TE.CL схем. Но при отключённой буферизации (часто делают для upload-endpoint'ов) или при HTTP/2 downgrade - вектор открывается. HAProxy с http-buffer-request и HTX-режимом тоже существенно сужает поверхность атаки, но не закрывает её полностью - особенно для H2 downgrade сценариев.Тестирование HTTP Request Smuggling на production - рискованная операция. Smuggled-запросы влияют на чужой трафик: можно сломать кэш, вызвать рассинхронизацию пайплайна, положить сервис для других пользователей. Всегда начинайте со staging, а на production - только с явным согласованием scope и готовностью к откату. Я однажды на production отравил кэш главной страницы клиента - починили за минуту, но адреналина хватило на неделю.
За последние два года вижу чёткий сдвиг: классические CL.TE/TE.CL на чистом HTTP/1.1 встречаются реже - Nginx и HAProxy в дефолтных конфигурациях стали достаточно строгими. Но количество HTTP/2 downgrade сценариев растёт пропорционально миграции на H2. Каждый CDN, каждый cloud-балансировщик - потенциальная точка конвертации H2 в H1.1 с собственными quirks парсера.
Большинство пентестеров до сих пор не тестируют H2.CL и H2.TE, потому что "HTTP/2 же иммунен к smuggling". По спецификации - да, если end-to-end. На практике end-to-end HTTP/2 до back-end - исключение, не правило.
Формула на бумаге понятна, но desync по-настоящему ощущается только когда сам прогоняешь timing-пробы и наблюдаешь, как пятисекундная задержка превращается в smuggled
GET /admin. Хочется руки в крови - лаба с этой техникой ждёт на HackerLab (ХакерЛаб).
AI-выжимка
по разделам
Тезисы статьи скоро
Статья читается полностью. Тезисы со ссылками на разделы появятся позже.
Содержание
Codeby Academy
Практика и мастерство
От основ до продвинутого — программы для практиков Codeby.
Перейти к курсу →
Продолжить чтение
Следующий разбор
Определение сервера, приложения и фреймворка
Комментарии
0