Cómo entender el contrabando de solicitudes HTTP
Sam Sanoop
30 de junio de 2020
0 minutos de lecturaEl contrabando de solicitudes HTTP es un tipo de vulnerabilidad interesante que ha ganado popularidad durante el último año. Esta vulnerabilidad podría permitir que un atacante aproveche características específicas del protocolo HTTP/1.1 para evadir protecciones de seguridad, realizar ataques de phishing y obtener información confidencial de solicitudes ajenas.
También cabe señalar que el contrabando de solicitudes ha recibido mucha atención de la comunidad gracias a numerosos informes de recompensas por errores de alto valor en los últimos meses. Snyk observó que, hasta ahora, en 2020 se han publicado 18 vulnerabilidades relacionadas con el contrabando de solicitudes HTTP en dependencias.
Watchfire fue la primera organización en hablar de esta vulnerabilidad, en su documento técnico de 2005 titulado “HTTP Request Smuggling”. Más adelante, el investigador Regis Leroy amplió este trabajo y James Kettle, de seguridad de Portswigger, lo analizó durante BlackHat USA 2019, lo que atrajo aún más atención de los medios.
Esta publicación busca aclarar en qué consiste esta vulnerabilidad y ofrecer detalles para corregirla a quienes mantienen proyectos de código abierto y quieren aplicar un parche contra el contrabando de solicitudes HTTP en sus proyectos.
Antecedentes
A medida que nuevos ataques y vulnerabilidades ganan popularidad y atraen la atención de los medios, los atacantes suelen apuntar a las bibliotecas de código abierto para explotarlas en entornos reales. A menudo, estas vulnerabilidades tardan semanas o años en corregirse debidamente en las bibliotecas, debido a la falta de conocimientos sobre cómo solucionarlas. Para contribuir a la seguridad de la comunidad de código abierto, el equipo de seguridad de Snyk suele investigar vulnerabilidades como el contrabando de solicitudes HTTP, con el fin de detectarlas en dependencias de código abierto y ofrecer a quienes las mantienen recomendaciones prácticas para corregirlas.
Una de estas tareas consistió en identificar el impacto del contrabando de solicitudes HTTP en el ecosistema de dependencias de código abierto, lo que llevó a Snyk a descubrir numerosas vulnerabilidades. Puedes encontrar aquí más información sobre las vulnerabilidades descubiertas.
Snyk descubrió que no hay suficientes fuentes públicas que ofrezcan orientación adecuada para corregir el contrabando de solicitudes HTTP. Además, se observó que quienes mantienen las bibliotecas necesitaron varias versiones para resolver y corregir debidamente esta vulnerabilidad. Con esta publicación, buscamos cerrar esa brecha y ofrecer recomendaciones detalladas a quienes necesiten orientación para abordar este problema.
Explicación de la vulnerabilidad de contrabando de solicitudes HTTP
Para entender el contrabando de solicitudes HTTP, primero hay que comprender los siguientes conceptos:
Conexiones persistentes y canalización
El concepto de conexiones persistentes y canalización se publicó inicialmente en RFC 2616.
El encabezado Keep-Alive es un encabezado salto a salto que proporciona información sobre una conexión persistente. En los servidores web, Keep-Alive se puede especificar en el encabezado “Connection”, lo que permite que un servidor web mantenga abierto un socket o una conexión TCP. Este encabezado permite que varias solicitudes y respuestas usen una sola conexión, lo que puede reducir la sobrecarga y mejorar el rendimiento de un servidor web. Actualmente, todos los navegadores y servidores admiten esta función.
La canalización es otra función que se introdujo en RFC 2616. Permite que un servidor web procese solicitudes de forma asíncrona, como un flujo de primero en entrar, primero en salir, en lugar de procesar cada solicitud de manera individual. Así, puede enviar una solicitud sin esperar a que llegue una respuesta anterior.
Content-Length y Transfer-Encoding
Las solicitudes HTTP pueden tener un cuerpo de mensaje. La presencia de un cuerpo de mensaje en una solicitud se indica mediante un campo de encabezado Content-Length o Transfer-Encoding. Estos encabezados se usan para delimitar los mensajes e indicarle al servidor dónde termina un mensaje y dónde empieza el siguiente.
Content-Length, especificado en RFC 7230, sección 3.3.2, es un encabezado HTTP que indica el tamaño del cuerpo de la entidad de la solicitud. Es común verlo en las solicitudes HTTP POST, que tienen un cuerpo de datos. Cabe señalar que, por lo general, las solicitudes GET no deberían incluir el encabezado Content-Length, ya que no tienen cuerpo.
Transfer-Encoding, también especificado en RFC 7230, se creó para permitir el envío de datos binarios a través de HTTP. Transfer-Encoding tiene varias directivas; en esta publicación nos centraremos en la directiva chunked.
La directiva chunked permite enviar datos en una serie de fragmentos, junto con la longitud de estos fragmentos especificada en formato hexadecimal, seguida de un retorno de carro y un salto de línea. Una directiva chunked termina con un 0 y una secuencia vacía. A continuación, se muestra un ejemplo de una solicitud chunked.
Contrabando de solicitudes
Una pila de servidores web moderna suele incluir varios servidores web, además de balanceadores de carga y servidores WSGI. Un diagrama básico para visualizarlo sería:

Las vulnerabilidades de contrabando de solicitudes HTTP aparecen cuando el frontend y el backend interpretan de manera diferente los límites de una solicitud HTTP, lo que provoca una desincronización entre ambos. Esto se debe a que varias bibliotecas de frontend y backend se desvían de las especificaciones RFC al procesar los encabezados Content-Length y Transfer-Encoding. Los cuerpos de las solicitudes HTTP pueden delimitarse según estos dos encabezados, y es posible que se produzcan desviaciones de la especificación. Como resultado, parte de una solicitud se agrega o se infiltra en la siguiente, lo que permite que la respuesta de la solicitud infiltrada llegue a otro usuario.
Esta vulnerabilidad se puede explotar para realizar ataques de phishing, envenenamiento de caché, secuencias de comandos entre sitios (XSS) y más. El año pasado, durante BlackHAT USA 2019, James Kettle publicó más información sobre cómo explotar esta vulnerabilidad en un artículo titulado “HTTP Desync Attacks: Request Smuggling Reborn”. Esta publicación se centrará en las dos técnicas de contrabando de solicitudes más comunes:
CL:CL: técnica de ataque con dos encabezados Content-Length
CL:TE: ataque con Content-Length y Transfer-
Encoding
CL:CL: técnica de ataque con dos encabezados Content-Length
Según RFC 7230, sección 3.3.3#4:
“Si se recibe un mensaje sin Transfer-Encoding y con varios campos de encabezado Content-Length cuyos valores son distintos, o con un solo campo de encabezado Content-Length cuyo valor no es válido, el encuadre del mensaje no es válido y el destinatario DEBE tratarlo como un error irrecuperable”
Sin embargo, la mayoría de los servidores web y el middleware actuales procesan de manera poco estricta las solicitudes GET con cuerpo. Además, RFC 7231, sección 4.3#4.3.1 establece que “una carga útil dentro del mensaje de una solicitud GET no tiene una semántica definida; enviar un cuerpo de carga útil en una solicitud GET podría hacer que algunas implementaciones existentes rechacen la solicitud”. Esto indica que la mayoría de los servidores y proxies podrían admitir este comportamiento. En algunos casos, esto puede dar lugar a ataques de contrabando de solicitudes. En esta publicación analizaremos una variante de este ataque.
Cuando se proporcionan dos encabezados Content-Length, pueden producirse ataques de contrabando si el frontend y el backend no coinciden en cuál de ellos debe tener prioridad.

En la solicitud HTTP anterior, se envía una solicitud con dos encabezados Content-Length a un destino cuyo frontend es un proxy o un balanceador de carga. El proxy respeta y prioriza el primer Content-Length, por lo que interpreta la solicitud infiltrada como parte del cuerpo de la solicitud, aunque una solicitud GET no debería tener cuerpo y se proporcionen dos encabezados Content-Length. Al procesarla, el backend ignora el primer encabezado Content-Length y prioriza el segundo. Como el segundo Content-Length está establecido en cero, el backend no espera un cuerpo de solicitud y trata la solicitud /reqsmuggle como otra solicitud canalizada. Por lo tanto, otro usuario podría recibir la respuesta de esta solicitud infiltrada.
CL:TE: técnica de ataque con Content-Length y Transfer-Encoding
Esta popular técnica, descubierta por James Kettle, de PortSwigger Labs, consiste en incluir un encabezado Content-Length y uno Transfer-Encoding en una sola solicitud HTTP y manipularla de tal manera que el proxy frontend y el servidor backend prioricen estos encabezados y procesen la solicitud infiltrada de forma distinta. A continuación, se muestra un ejemplo de solicitud que ilustra esta vulnerabilidad.
Cuando un proxy frontend procesa esta solicitud, respeta el encabezado Content-Length y le da prioridad sobre Transfer-Encoding. Sin embargo, el servidor backend prioriza el encabezado Transfer-Encoding. Por lo tanto, el backend procesa la solicitud y se detiene en el carácter 0. Como resultado, la solicitud GET 404 se trata como una solicitud canalizada independiente.
Este problema ocurre porque el frontend prioriza Content-Length sobre el encabezado Transfer-Encoding. Según RFC 7230, sección 3.3.3#3, si se acepta un mensaje con Content-Length y Transfer-Encoding, el encabezado Transfer-Encoding prevalece sobre Content-Length. El frontend no sigue esta regla.

En los casos en que el frontend priorice el encabezado Transfer-Encoding, quizá sea posible evadir esta limitación insertando un encabezado de codificación de transferencia mal formado en lugar de uno válido. A continuación, se muestran algunos ejemplos:
Muchas bibliotecas HTTP toleran distintas variantes del encabezado Transfer-Encoding y las normalizan para mejorar la experiencia del cliente. Por ello, si se entiende qué variantes del encabezado Transfer-Encoding normaliza el servidor backend, quizá sea posible infiltrar un encabezado Transfer-Encoding mal formado a través del frontend y realizar un ataque de contrabando CL:TE.
Los ejemplos anteriores solo ilustran la vulnerabilidad. En un ataque real, se puede crear una solicitud infiltrada para realizar ataques de phishing o robar datos de la solicitud de una víctima y enviarlos a un servidor controlado por el atacante.
Alcance y conceptos erróneos
Durante esta investigación, Snyk identificó varios casos en los que la evidente complejidad de los múltiples vectores de ataque dio lugar a conceptos erróneos sobre el contrabando de solicitudes HTTP. Investigadores y quienes mantienen proyectos de código abierto informaban sobre el problema sin tener siempre claro si los criterios para corregirlo correspondían al alcance de la biblioteca que mantenían o si debía abordarse en una dependencia que esta usaba.
Algunos de los desafíos que requirieron más aclaraciones son los siguientes:
Los proxies, los balanceadores de carga y otros componentes que pueden clasificarse como frontend son los más afectados por esta vulnerabilidad. Sin embargo, para que un ataque de contrabando de solicitudes tenga éxito, es necesario explotar tanto un frontend como un backend vulnerables.
La responsabilidad de corregir el problema recae tanto en quienes mantienen el backend como en quienes mantienen el frontend. Se podría argumentar que normalizar los encabezados mal formados debería ser un comportamiento aceptable del backend para que sea más tolerante con los errores de los usuarios, y que el verdadero problema son los proxies que reenvían estas solicitudes sin normalizarlas primero. Sin embargo, dada la amplitud del ecosistema de dependencias y la gran cantidad de bibliotecas que pueden funcionar como backend y frontend, lo ideal es que ambas partes intenten corregir este problema.
Se considera que el middleware, los servidores web con capacidades de proxy inverso y los servidores WSGI/de alto rendimiento cumplen los criterios de un backend, pero no un servidor web que solo admita procesar solicitudes mal formadas y no admita la canalización.
A menudo se informa a quienes mantienen los sistemas sobre problemas de contrabando de solicitudes HTTP porque los servidores responden a varias solicitudes enviadas y esto se ve como dos respuestas independientes. Cabe señalar que muchos servidores admiten Keep-Alive y la canalización; esto, por sí solo, no constituye una vulnerabilidad de contrabando de solicitudes HTTP. Este es el caso de CVE-2020-12440, reportada para NGINX.
Corrección
Los problemas de contrabando de solicitudes HTTP se producen por desviaciones de las especificaciones HTTP vigentes y porque varias bibliotecas no cumplen con la RFC 7230. Además, la solución de esta vulnerabilidad puede ser complicada, según si mantienes un proyecto de frontend o backend. Por eso, Snyk investigó las medidas correctivas implementadas actualmente por proyectos de código abierto y las clasificó de la siguiente manera. Para simplificar, se incluyen todas las medidas, junto con una explicación del tipo de ataque de contrabando que ayuda a prevenir cada una. En el escenario ideal, se deberían aplicar todos los puntos que se mencionan a continuación para implementar una solución de defensa en profundidad.
Priorizar Transfer-Encoding sobre Content-LengthMedida correctiva: Esta medida evitará los ataques CL:TE y TE:CLAlcance: Frontend, backendDetalles: Cuando se recibe una solicitud con los encabezados Transfer-Encoding: chunked y Content-Length, se debe dar prioridad al encabezado Transfer-Encoding sobre Content-Length. Esto se establece en la sección 3.3.3#3 de la RFC 7230.
Rechazar solicitudes con Content-Length y Transfer-Encoding, y con encabezados Content-Length duplicadosMedida correctiva: Esta medida evitará los ataques CL:CL, CL:TE y TE:CLAlcance: Backend, frontend, backend y bibliotecas upstreamDetalles: Esta puede considerarse una alternativa mejor a la solución «Priorizar Transfer-Encoding sobre Content-Length». Plataformas de ejecución como Node.js han utilizado esta solución para corregir la vulnerabilidad de contrabando de solicitudes: las solicitudes que contienen ambos encabezados reciben una respuesta HTTP 400.
Snyk considera que esta técnica es una solución ideal para prevenir problemas de contrabando. Sin embargo, hay que tener en cuenta cómo se aplica, sobre todo si la corrección se implementa en una biblioteca HTTP de bajo nivel o en un motor upstream del que dependen numerosos paquetes, como los de frontend.
Al corregir problemas de contrabando de solicitudes HTTP, se debe seguir la RFC 7230 #3.3.3. La sección 3.3.3#3 de la RFC 7230 establece lo siguiente:
«Si se recibe un mensaje con campos de encabezado Transfer-Encoding y Content-Length, Transfer-Encoding prevalece sobre Content-Length. Este mensaje podría indicar un intento de llevar a cabo un contrabando de solicitudes (sección 9.5) o una división de respuestas (sección 9.4), y debería tratarse como un error. El emisor DEBE eliminar el campo Content-Length recibido antes de reenviar el mensaje a un sistema posterior».
Sin embargo, esto difiere de lo que establece la RFC 2616 4.4#3:
«Si se recibe un mensaje con campos de encabezado Transfer-Encoding y Content-Length, se DEBE ignorar este último».
Cabe señalar que la RFC 2616 4.4#3 está obsoleta y fue reemplazada por la RFC 7230. Al implementar una corrección, se debe tener en cuenta la RFC 7230. Por lo tanto, al procesar solicitudes HTTP que contienen los encabezados Content-Length y Transfer-Encoding, el comportamiento correcto es que el frontend elimine el encabezado Content-Length antes de reenviar la solicitud a un backend posterior, no que devuelva un encabezado de respuesta HTTP 400. Lo mismo debe hacerse con las solicitudes que contienen varios encabezados Content-Length, según lo establecido en la RFC 7230#3.3.2.
Como se menciona en la sección 3.3.2 de la RFC 7230, si se recibe una solicitud HTTP con varios encabezados Content-Length con valores de longitud distintos, se puede corregir devolviendo una respuesta HTTP 400 o reemplazando los valores duplicados por un único campo Content-Length válido. Snyk recomienda que las bibliotecas HTTP de bajo nivel reemplacen los encabezados múltiples por uno solo válido.
Rechazar encabezados Transfer-Encoding malformados y procesar correctamente varios valores de TEMedida correctiva: Esta medida evitará los ataques TE:TE.Alcance: Frontend, backendDetalles: Si tanto el frontend como el backend dan prioridad al encabezado Transfer-Encoding, podrían permitir ataques de contrabando en los que un atacante inserta dos encabezados Transfer-Encoding: uno que el frontend ignora y el backend procesa, o viceversa. Por eso, se deben rechazar las siguientes variantes de encabezados.

Sin embargo, cabe señalar que los atacantes aún pueden encontrar una variante malformada del encabezado de codificación «chunked» que no esté documentada anteriormente. Por lo tanto, esta medida por sí sola no debe considerarse suficiente para corregir la vulnerabilidad. Una mejor opción sería rechazar las solicitudes que incluyan tanto Content-Length como Transfer-Encoding, además de los encabezados malformados.
Por último, también cabe señalar que si el valor «chunked» se especifica junto con otros valores de Transfer-Encoding, como «gzip» o «deflate», no se debe pasar por alto y se debe dar prioridad a Transfer-Encoding sobre Content-Length. Un ejemplo de esta vulnerabilidad se encuentra en CVE-2019-16786. Si se envía una solicitud HTTP con los siguientes valores de Transfer-Encoding, se debe identificar correctamente el valor «chunked» y darle prioridad:
Transfer-Encoding: gzip, chunked
En un encabezado Transfer-Encoding se pueden incluir varios valores separados por comas. Sin embargo, puede ocurrir que el frontend solo identifique el valor «gzip» y, por lo tanto, dé prioridad al encabezado Content-Length, mientras que el backend procese la solicitud como chunked y provoque un ataque CL:TE. Snyk recomienda verificar correctamente los casos en que se especifican varios valores y dar prioridad a Transfer-Encoding si se incluye «chunked». Además, si se especifican varios encabezados Transfer-Encoding, el valor «chunked» solo debe aparecer al final, después de los demás valores de codificación de transferencia. Esto se establece en la sección 3.3.1 de la RFC 7230.
Conclusión
En conclusión, el contrabando de solicitudes HTTP puede ser una vulnerabilidad confusa de entender y corregir. Esta publicación busca ayudar a quienes mantienen proyectos a escribir correcciones eficaces y proteger sus proyectos de código abierto.
También vale la pena señalar que esta publicación solo abarca dos técnicas de contrabando de solicitudes HTTP y no ofrece información detallada sobre los distintos escenarios de explotación. El siguiente artículo del investigador de seguridad ZeddYu analiza a fondo el contrabando de solicitudes HTTP y ofrece detalles exhaustivos sobre cada vector de ataque: Zeddy Yu: Ayuda para entender el contrabando HTTP en un solo artículo.
Comienza con Capture the Flag
Aprende a resolver desafíos de Capture the Flag con nuestro taller virtual 101 a pedido.



