Небольшой словарик (подробнее о каждом термине в статье):
HTTP request smuggling — тип уязвимости, при которой злоумышленник вмешивается в обработку последовательности HTTP‑запросов, которую веб‑приложение получает от одного или нескольких пользователей.
HTTP keep‑alive — возможность использовать одно TCP‑соединение для отправки и получения многократных HTTP‑запросов и ответов вместо открытия нового соединения для каждой пары запрос‑ответ.
HTTP Pipelining — технология, при которой несколько HTTP‑запросов отправляются по одному TCP‑соединению без ожидания соответствующих ответов.
Иногда кажется, что найден HTTP request smuggling, а на деле это всего лишь HTTP keep‑alive или pipelining. Обычно это ложное срабатывание, но иногда за этим действительно скрывается уязвимость. Здесь разбирается, как отличить одно от другого.
Ложные срабатывания из‑за переиспользования соединений
Если демонстрация уязвимости (PoC) для request smuggling работает только при переиспользовании соединения, скорее всего, это ложноположительный результат. Вот распространённые случаи connection reuse (Повторное использование соединений HTTP):
Turbo Intruder (расширение для Burp Suite, которое позволяет отправлять большое количество HTTP‑запросов и анализировать результаты) с requestsPerConnection больше 1.
Burp Intruder при включённом HTTP/1 Connection Reuse.
Burp Repeater с опциями «Send group in sequence (single connection)» или «Enable connection reuse».
Атаки в Burp Repeater, где видно два HTTP/1-ответа подряд в одном сеансе.
Однако это не всегда ложное срабатывание. Есть три близких класса уязвимостей, для которых connection reuse действительно необходимо:
Connection-locked request smuggling.
Атаки на состояние соединения (формально это не request smuggling).
Client-side desync-атаки.
Поэтому, создавая PoC для request smuggling, всегда отключайте connection reuse, когда это возможно. Если атака перестала работать, у вас два пути: остановиться или копнуть глубже.
Сonnection reuse в HTTP/1
Большинство инструментов представляют HTTP/1-запросы как отдельные, изолированные сущности. Обычно это удобная абстракция, но request smuggling пытается её нарушить, поэтому важно понимать нижележащий уровень.
В помощь запущен HTTP Hacker — новое расширение для Burp Suite, показывающее низкоуровневое поведение HTTP.
Под капотом HTTP/1.1 переиспользует соединения, просто конкатенируя запросы и ответы поверх TCP/TLS-сокета. Это известно как connection reuse, pipelining или keep-alive. Пример:
POST / HTTP/1.1
Host: hackxor.net
Connection: keep-alive
Content-Length: 5
12345GET /robots.txt HTTP/1.1
Host: hackxor.net
Pipelining — это подвид connection reuse, при котором клиент отправляет все запросы залпом и полагается на то, что ответы придут в нужном порядке. Большинство серверов умеют обрабатывать конвейерные запросы, но реальных клиентов, которые их посылают, немного — именно это делает Turbo Intruder таким быстрым.
Разобравшись с основами, посмотрим, что произойдёт, если отправить атаку CL.0 дважды и переиспользовать соединение:
POST / HTTP/1.1
Host: hackxor.net
Content_Length: 47
GET /robots.txt HTTP/1.1
X: Y
Первый ответ:
HTTP/1.1 200 OK
Content-Type: text/html
Второй ответ:
HTTP/1.1 200 OK
Content-Type: text/plain
User-agent: *
Disallow: /settings
Мы видим, что по крайней мере один сервер проигнорировал некорректный заголовок Content_Length. Может показаться, что вы создали десинхронизацию между фронтендом и бэкендом.

Однако на самом деле вы лишь вызвали десинхронизацию между вашим HTTP-клиентом и целевым сервером.

Вот исходный поток запросов:
POST / HTTP/1.1
Host: hackxor.net
Content_Length: 47
GET /robots.txt HTTP/1.1
X: YPOST / HTTP/1.1
Host: hackxor.net
Content_Length: 47
GET /robots.txt HTTP/1.1
X: Y
Это бесполезно. Более того, у hackxor вообще нет бэкенда, так что он иммунен к request smuggling.
Это проясняет, почему переиспользование клиентских соединений может приводить к ложным срабатываниям.
Request smuggling, привязанный к соединению
Было бы удобно сказать «никогда не переиспользуйте соединения при тестировании на request smuggling», но всё сложнее.
Некоторые фронтенд-серверы переиспользуют апстрим-соединение только если было переиспользовано клиентское соединение. В итоге возникают уязвимости request smuggling, которые срабатывают только при переиспользовании соединения со стороны клиента. Этот сценарий автор называет connection-locked request smuggling.
Попробуйте отправить запрос по HTTP/2, который спровоцирует ответ, содержащий вложенный отдельный HTTP/1-ответ. Это доказывает, что это не ложное срабатывание, и значит, стоит попытаться построить эксплойт. Альтернативно, нередко удаётся отличить connection-locked request smuggling с помощью частичных запросов.
Однако для доказательства реальной уязвимости нужны свидетельства практического воздействия, выходящие за рамки «атакующий может получить неожиданный ответ». Connection-locked request smuggling не позволяет напрямую атаковать других пользователей, но всё ещё можно добиваться эффекта за счёт отравления серверного кэша (если он есть), эксплуатации отражения ввода для раскрытия внутренних HTTP-заголовков, обхода защитных мер, применяемых фронтендом.
Это путь к валидному отчёту. Если вы считаете, что нашли connection-locked request smuggling, действуйте так. Исследуйте в Turbo Intruder с requestsPerConnection=2 или в группе вкладок Repeater через «Send group in sequence (single connection)».
Атаки на состояние соединения
Исследуя connection-locked request smuggling, вы можете обнаружить атаки на состояние соединения, например «маршрутизацию первого запроса». Они возникают, когда сервер обрабатывает первый запрос на соединении иначе, чем последующие. Формально это не уязвимости request smuggling и возможны даже там, где нет фронтенда, но по факту их влияние схоже с connection-locked request smuggling.
Desync-атаки на стороне клиента
Есть ещё один эксплуатируемый сценарий, где требуется connection reuse, — client-side desync. Важное ограничение: вредоносный запрос должен быть тем, что браузер жертвы сможет отправить междоменным образом. На практике это исключает любые приёмы обфускации заголовков.