
Detecta y explota vulnerabilidades de contrabando de solicitudes HTTP mediante la conversión de HTTP/2 a HTTP/1.1, utilizando técnicas automatizadas de contrabando de cabeceras para identificar discrepancias en el análisis del backend.
Esta herramienta ayuda a detectar y explotar el contrabando de solicitudes HTTP en aquellos casos en que se puede lograr mediante la conversión HTTP/2 -> HTTP/1.1 por parte del servidor frontend.
El esquema es el siguiente:
El atacante busca encontrar una solicitud tal que sea vista como dos solicitudes separadas por el servidor backend.
Si la conexión HTTP/1.1 frontend<->backend utiliza keep-alive, el frontend podría enviar solicitudes de otros usuarios en la misma conexión. Si somos capaces de "envenenar" la conexión con una solicitud parcial que llega después de una legítima, podemos recuperar la solicitud de otro usuario.
Otros escenarios posibles incluyen la omisión de la protección del servidor frontend y sus reescrituras, el envenenamiento de caché o el engaño de caché.
Para obtener más información sobre el contrabando de solicitudes HTTP, consulte la Academia de Seguridad Web de Portswigger.
En HTTP/2, todos los nombres y valores de las cabeceras HTTP son binarios. Eso significa que técnicamente pueden contener espacios adicionales o incluso saltos de línea.
RFC7540#10.3 establece que las implementaciones que traducen solicitudes HTTP/2 a HTTP/1 deben tener en cuenta las limitaciones en el conjunto de caracteres que surgen de dicha conversión; la mayoría de las implementaciones efectivamente los rechazan. A pesar de esto, esperamos encontrar algunas que permitan dichas cabeceras. Estas corromperán la solicitud HTTP/1.1 convertida hacia el backend.
Otro punto es que algunas correcciones recientes relacionadas con el contrabando de solicitudes HTTP podrían implementarse solo para los analizadores HTTP/1.1.
En general, esperamos que existan implementaciones de HTTP/2 que no estén muy al tanto de las investigaciones recientes sobre contrabando de solicitudes HTTP en HTTP/1.1 y no incluyan las mitigaciones correspondientes.
¡Sorprendentemente, sí!
Encontré una posibilidad de introducir de contrabando una cabecera con un carácter de espacio a través de Cloudflare, abriendo así la puerta al contrabando entre Cloudflare y el cliente (en caso de que el software de un cliente de Cloudflare acepte y recorte nombres de cabeceras). Aquí está la publicación del blog.
También hay otro informe de recompensa por errores que aún no es público. Este hace uso del hecho de que el software personalizado no filtra los saltos de línea en las cabeceras HTTP/2, y el contrabando ocurre al 100% (puedo ver las solicitudes de otros usuarios).
Sin embargo, entiendo que este tipo de vulnerabilidades deben ser frustrantemente raras: a diferencia de HTTP/1.1, no hay tantas implementaciones de HTTP/2, y la mayoría están diseñadas teniendo en cuenta la seguridad, por lo que rechazan cabeceras sospechosas o inválidas.
La herramienta tiene un subcomando que intenta detectar automáticamente si un objetivo es vulnerable al ataque de contrabando de solicitudes HTTP. El algoritmo detrás de esta funcionalidad se describe en esta sección.
Para realizar un ataque de contrabando de solicitudes HTTP, primero necesitamos "introducir de contrabando" una única cabecera (ya sea Content-Length o Transfer-Encoding). Esto significa que necesitamos enviar una cabecera que a) controle dónde termina el cuerpo de la solicitud y b) no sea procesada por el frontend pero sí por el backend.
Esto generalmente se logra modificando una cabecera de alguna manera: agregando espacios o tabulaciones al final de su nombre, reemplazando el valor por uno semi-equivalente, etc.
La idea básica del algoritmo de detección de vulnerabilidades es detectar si el servidor realmente procesa una cabecera de contrabando como si fuera Content-Length o Transfer-Encoding. Hacemos esto enviando múltiples solicitudes: algunas con valores válidos y otras con valores inválidos para la cabecera. Luego intentamos detectar si hay alguna forma de distinguir las respuestas de estos dos grupos.
Por eso la salida de la herramienta no contiene las palabras "vulnerable/invulnerable": solo indica si puede distinguir las respuestas que llegaron de las solicitudes de estos dos grupos.
La herramienta considera que dos conjuntos de respuestas HTTP son distinguibles si se cumple al menos una de las siguientes dos condiciones:
Los tiempos de espera se tratan como un valor de código de estado único, no igual a ningún otro; por lo tanto, la herramienta reemplaza el esquema clásico de "detección por temporización".
Consideremos un ejemplo. Supongamos que estamos tratando de introducir de contrabando la cabecera transfer-encoding reemplazando el guion por un guion bajo.
Si resulta que el servidor responde con el estado 400 cada vez que enviamos transfer_encoding:zalupa y se cuelga cuando es transfer_encoding:chunked, podemos decir que probablemente el servidor procesa la cabecera como el valor para transfer encoding. Teóricamente, podría ser el servidor frontend o el backend.
El primer caso no es interesante ya que podríamos enviar la versión no contrabandeada de la cabecera de todos modos, y el segundo caso es lo que estamos buscando. Como todo ocurre a través de HTTP/2, el primer caso se puede evitar la mayoría de las veces: el servidor HTTP/2 determina el final del cuerpo de la solicitud de otra manera que no está relacionada con las cabeceras de la solicitud y nunca espera que el cuerpo esté en el formato fragmentado HTTP/1.1 (aquel con longitudes hexadecimales de fragmento).
Las variaciones concretas de las técnicas de detección son:
Enviamos una versión contrabandeada de Transfer-Encoding: chunked (p. ej., transfer_encoding:chunked) y diferentes cuerpos: válido es 0\r\n\r\n e inválido es 999\r\n.
En caso de que las respuestas sean diferentes, podemos estar seguros de que el servidor backend recibe y procesa la cabecera contrabandeada. No hay razón para que el frontend haga esto: HTTP/2 no utiliza el formato fragmentado que enviamos, por lo que sería inválido.
Esperamos que el backend se cuelgue (es decir, que la solicitud llegue a tiempo de espera) para las solicitudes inválidas, ya que espera que lleguen más datos.
Esta es la variante de detección más fiable: si el servidor se cuelga al leer el cuerpo, algo probablemente ha ido mal, ya que no hay uso para la codificación de transferencia HTTP/1.1 en una solicitud HTTP/2.
Enviamos una versión contrabandeada de Transfer-Encoding y nuevamente diferentes cuerpos: 0\r\n\r\n como cuerpo válido y X\r\n\r\n como inválido.
El caso es el mismo que el anterior, pero en lugar de leer el cuerpo, esperamos que el backend al menos lo valide.
Enviamos una versión contrabandeada de la cabecera Content-Length con valores 1 y -1.
Ambos valores son inválidos desde el punto de vista del frontend: hay otro mecanismo para determinar la longitud del cuerpo en HTTP/2, y en ambos casos no se envía ningún cuerpo de solicitud. Si las respuestas son diferentes, suponemos que es el servidor backend quien analizó las cabeceras.
Este método es el menos fiable: el frontend podría emitir diferentes errores cuando Content-Length tiene un valor inválido y cuando no coincide con la longitud real.