Blind SSRF en Microsoft Ads


Server-Side Request Forgery para Pentesters
En este artículo, mostraré el roadmap de la explotación de un Blind SSRF encontrado en Microsoft Ads. Este análisis explica los controles implementados en la aplicación, así como también expone como esos controles fueron evadidos para enumerar endpoints en la red local del backend vulnerable.
El Server-Side Request Forgery no es vulnerabilidad nueva para nosotros. En una anterior ocasión, ya hemos escrito en este blog sobre un SSRF que encontré en msn.com y con el cual pudimos llegar a la red local del Web Server. En aquellos días, esta vulnerabilidad estaba poco explotada y los controles implementados para mitigarla, eran realmente pensando en otras vulnerabilidades y no en SSRF como tal.
Cada vez hay más controles que limitan la posibilidad de explotación real de esta vulnerabilidad y ahora se requiere un mayor entendimiento del entorno y conocimiento preciso del flujo de confianza del servicio, para poder explotar este tipo de vulnerabilidades.
Blind SSRF y sus complicaciones
En un escenario de blind SSRF, la explotación no se consigue enviando una URL arbitraria y esperando que algo ocurra. Dado que en este tipo de vulnerabilidad no se puede visualizar la respuesta de la petición, el análisis debe enfocarse en el comportamiento del componente para lograr inferir con la mayor certeza posible, la lógica con la que el backend recibe, procesa y ejecuta la solicitud. Para que esa ejecución ocurra, el payload debe cumplir con los criterios internos del sistema que lo interpreta.
Esto implica entender con precisión qué condiciones hacen que una URL sea aceptada y procesada: desde la validez del hostname, la estructura del recurso (body), el formato de datos que recibe (filetype), el procesamiento de extensiones específicas (filename) o esquemas particulares. El objetivo no es forzar el sistema, sino alinear la trama con la expectativa del endpoint receptor para que ejecute una acción específica que el atacante quiere. Así es como se puede adquirir el mayor control de la ejecución de la trama. Explotar un blind SSRF de forma efectiva, requiere diseñar una respuesta de nuestro server que se integre naturalmente en la lógica del flujo interno.
Análisis del filtro de las URLs.
Pasando al análisis técnico del comportamiento del servicio que identificamos como vulnerable en Microsoft Ads, lo primero interesante a mencionar, es que que el endpoint contaba con un control que evitaba que el recurso ingresado para ser procesado tuviese una URL que usara una IP como "hostname". De esta forma el backend evitaba que requests fuesen procesados y enviados si eran solicitados a una IP y no a un dominio. Este control dificulta realizar un escaneo a IPs locales de forma directa.
Funciona:
GET /path/process/r?url=http%3A%2F%2Fattacker.com&var=true&var=MD5
No funciona:
GET /path/process/r?url=http%3A%2F%2F127.0.0.1&var=true&var=MD5
Además, el endpoint tenia un segundo control en el cual solo procesaba de forma adecuada las URLs (seguía el flujo completo de lógica) si la URL contenía en el nombre del archivo, un formato específico.
Funciona:
GET /path/process/r?url=http%3A%2F%2Fattacker.com/resource.zip&var=true&var=MD5
No funciona:
GET /path/process/r?url=http%3A%2F%2Fattacker.com/resource.txt&var=true&var=MD5
En resumen, el endpoint recibe una URL. Pero solo la procesa, si el hostname es un dominio y si el archivo de la url es del formato previamente definido en la configuración del Endpoint.
Bypass con 302 Redirect (Request Control)
Cuando la explotación de un SSRF es exitosa, un atacante logra tomar control sobre las peticiones que realiza un endpoint y las reutiliza para obtener o modificar información confidencial del target. En este caso, aunque podría subir un archivo con el filetype necesario y ponerle un dominio a la URL, no podría hacer nada más que recibir un request del bot de Microsoft y ya.
Ya en otra ocasión, había tenido una situación similar y uno de los aprendizajes más valiosos que me dejó, fue que muchos de estos controles implementados en los backends, solo validaban el primer response que recibía del servicio consultado. Si la respuesta del servidor era un redirect, el control no aplicaba dado que el primer response en teoría si cumplía con lo necesario para ser procesado.
Entonces, en la vulnerabilidad encontrada en Microsoft Ads, ya habíamos identificado los controles que teníamos que evadir y ahora también tenemos la solución (teórica) que nos ayudaría a hacerles bypass: 302 Redirect. Para recapitular, nuestra URL, debe tener en la URL un filetype específico (.zip en este caso) y también debe ser un dominio y no una IP.
Para solucionar lo del dominio, pueden usar un dominio propio o usar alguno gratuito, si tienen la duda de cual es mejor, propio o gratuito, la realidad es que depende. Hay endpoints de lógica b2b que tienen blacklisteados muchos dominios gratuitos y hay otros que consumen en su mayoría recursos de fuentes gratuitas por lo que es mejor usar dominios que sean conocidos como camuflaje, por eso como antes mencioné, es super importante entender bien el endpoint que estamos trabajando.
El tema del zip es donde viene el bypass divertido, tenemos que hacerle creer que somos un .zip pero al mismo tiempo no serlo. Para lograr esto, en mi caso usé el apoyo de un pequeño script en python que aceptaba todos los paths que recibiera con un 302. Si el "bot" visita /ejemplo.zip, será redireccionado a cualquier destino que queramos.
Este código es parte de redirector.py:
Este script en mi server (dominio attacker.com), si es ejecutado de la siguiente manera:
redirector.py https://127.0.0.1:443/admin
Al recibir un request usando el servicio vulnerable, ej:
GET /path/process/r?url=http%3A%2F%2Fattacker.com/resource.zip&var=true&var=MD5
El bot cuando ingrese a ese link, recibirá una redirección 302 y será como si realmente hubiésemos enviado:
GET /path/process/r?url=http%3A%2F%2F127.0.0.1:443/admin&var=true&var=MD5
Aquí les comparto una imagen real del "bot" siendo redirigido a mi Burp collaborator. Interesante ver que es un Python-Requests y que no se han tomado el tiempo de cambiar el user-agent, interesante por todo lo que eso representa...

Ok, validado que tenemos el control del "bot", y también que el bypass en si funcionó ¿Cómo usarlo para que aumente el impacto de la vulnerabilidad?
Pivot y Time-Based Port Scan
Si, es posible realizar un port scan usando esta técnica de redirect con el escenario tal y como lo tenemos en este momento. Para ejecutar el port scan, el plan es el siguiente:
1. Hacer que el request que manda el bot llegue al web server controlado por nosotros.
2. Redireccionar el request a IPLOCAL:PUERTO
3. Tomar nota del tiempo que demoro en regresar la respuesta al bot.
¿Por qué el tiempo es importante? porque normalmente si un request consulta un puerto abierto, la respuesta que recibe es inmediata, en cambio si el puerto está cerrado, hay timeout y la respuesta demorará en llegar.
¿Cómo medir el tiempo? esa pregunta es interesante hay varias maneras, pero el flujo que apliqué en esta vulnerabilidad fue el siguiente:
1. El request vulnerable llevarlo a Intruder de burp suite.
2. Configurar el intruder para mandar unos request con este formato:
GET /path/process/r?url=http%3A%2F%2Fattacker.com/$PAYLOAD$/resource.zip&var=true&var=MD5
3. Configurar redirector,py para que el path de la url que reciba lo extraiga y lo ponga como puerto a una IP en específico.
4. Leer en el intruder el tiempo de respuesta de cada request que envió.
Es referencial, pero el código del redirector.py se ve algo así:

Con esta técnica en este momento y siguiendo la línea de tiempo como la he planteado, podríamos escanearle 65k puertos a 127.0.0.1, luego hacer lo mismo con cada una de las IPs, pero... ¿cual es la IP local de ese server? ¿Este SSRF nos puede servir de pivot para llegar a una red crítica? ¿como puedo atacar algún asset importante? A la hora de un Pentest o un Bug Bounty, un elemento clave, es demostrar el verdadero riesgo que representa una vulnerabilidad. y para un Blind SSRF no es tarea sencilla.
Quiero hacer énfasis una vez mas en la importancia que tiene entender el target y el endpoint que se está analizando. ¿Por qué? porque cuando escaneas esta clase de targets, con esta clase de método y todo a través de un SSRF, no siempre es fácil demostrar todo lo que se puede hacer. Entonces, aquí el elemento clave para explotar esta vulnerabilidad al máximo, es hacer ese network mapping para saber donde estamos y a que servicios podemos atacar.
En el caso de la vulnerabilidad en Microsoft Ads, busqué dos puertos específicos a distintos rangos de IPs locales, el 22 para buscar servidores con ssh a los que pudiese llegar y el 445 para buscar máquinas de personal que estuviesen conectadas a un domain controller. Así podría descartar rangos enteros de IPs que no tendrían mucho que ofrecer.
Aquí les comparto una imagen de la vulnerabilidad real, el cuadro verde son los puertos abiertos y el rojo son los cerrados, presten atención a como los tiempos de respuesta del 80 y 443 son menos de medio segundo vs los otros que demoran 3 segundos en volver. Podemos concluir que el server que estamos escaneando es un Web Server.

Análisis del código vulnerable
El bypass usando redirect 302, ocurre porque el código permitía que el request haga "follow" a los redirects. En la mayoría de librerías y clientes-cli que hacen conexiones HTTP, es posible configurarlos para que no sigan redirecciones.
He escrito un ejemplo de código vulnerable, para que se comprenda como funciona la vulnerabilidad desde distintos ángulos y también lo pueden usar para practicar. El código simula un endpoint llamado descargar, con una variable por get (url) que solo acepta links que terminen en ".zip". Si ejecutas el código, se levantará un web server en "https://vulnerable.com/descargar?url=" para explotar el código vulnerable, usa un script que redireccione todas las visitas hacia "https://127.0.0.1/admin", al visitar https://vulnerable.com/descargar?url=https://attacker.com/exploit.zip estarás visitando el panel admin de localhost.
Código vulnerable:
Ejemplo de redirect.py
Para solucionar el problema aquí y evitar que la aplicación no siga la redirección, habría que añadir el flag de allow_redirects en False al momento de lanzar el request. Esto sería cambiando la linea 19 de la siguiente manera:
response = requests.get(user_url, timeout=5, allow_redirects=False)
Conclusiones finales y Recomendaciones
Para los Pentesters, sepan que no es opcional entender el flujo completo de las aplicaciones que auditan, siempre debemos tener una idea clara sobre lo que pasa cuando se manda un payload, un buen Pentester es full-stack. Para los desarrolladores, siempre es bueno buscar las buenas prácticas de las tecnologías que se usan. De esa manera no se pasarán esta clase de vulnerabilidades que pueden ser muy sencillas de evitar con solo un flag.
Esta vulnerabilidad, fue reportada a Microsoft y ya implementó un fix. En abril de este año, aparecí en la lista de reconocimiento de Microsoft (MSRC) por ese reporte.

Todos los Pentest realizados por el equipo de Deep Security, son ejecutados con un nivel técnico avalado por la experiencia de un equipo que reporta vulnerabilidades a las empresas más grandes del mundo.
Si necesitas un Pentest profesional y acompañamiento continuo para mantener la superficie en internet de tu compañía segura, escríbenos y nuestro equipo comercial te contactará.




