Archive

Posts Tagged ‘xss’

Detecting and Fixing XSS using OWASP tools

Much have been written about XSS vulnerabilities scanning. In this article we will try to go a little further and show how to fix them.

To illustrate the whole process, going from initial detection to providing a fix, we will use a very simple app consisting of two JSP pages: one is a payment form for credit card transactions and contains some XSS exploitable code. The other one has such code fixed: the later is just a patched version of the former. We will see how an attacker could trick users, exploiting the present XSS vulnerability, to collect their credit card data.

(Download vulnerable app)

XSS Attacks

 

The goal of XSS attacks is to have a injected script executed by the user web browser. In most cases, user is not even aware of what is going on. For further information about XSS please have a look at OWASP XSS Attack Description.

Let us have a look at our sample app. The vulnerable JSP (xss_html.jsp) contains the following code fragment:


<% 
final String amount = request.getParameter("amount"); 
Enumeration pNames = request.getParameterNames(); 
while (pNames.hasMoreElements()){     
   final String pName = pNames.nextElement();     
   final String pVal = request.getParameter(pName);     
%>
    <input type="hidden" name="<%=pName%>" />" value="" />;</pre>
<table>
<tbody>
<tr>
<td>Credit card</td>
<td><input type="text" maxlength="16" name="cc" size="16" value="" /></td>
</tr>
<tr>
<td>Exp Date (mm/yy)</td>
<td><input type="text" maxlength="2" name="expMonth" size="2" value="" />
/<input type="text" maxlength="2" name="expYear" size="2" value="" /></td>
</tr>
<tr>
<td>CVV2</td>
<td><input type="text" maxlength="2" name="expMonth" size="2" value="" />
/<input type="text" maxlength="2" name="expYear" size="2" value="" /></td>
</tr>
<tr>
<td colspan="2"><input id="button1" type="submit" name="button1" value="Pay" /></td>
</tr>
</tbody>
</table>

The form receives amount to charge as an HTTP Request parameter, collects credit card data form user and them charges her (of course, such last step is not included, you can try with any non-real credit card data). Users could be redirected here from any ecommerce site using a URL such as http://……/XSS_Vulnerable/xss_html.jsp?amount=12.25 (again, nobody would choose such a path for a payment gateway pretending to be a trusted one). But let us see what happens if an attacker tricks someone into loading a malicious URL as the one included in index.jsp instead when the user press Pay button (Firefox 24.0 for Linux):

firefox_vulnerable

Just an alert, but injected Javascript code could have created an image or some other kind of link, so the attacker would have been able to collect the data by just looking at Apache access logs. Indeed, some browsers are able to identify such threats and will not execute injected scripts, as shown (Chromium 28.0 for Linux).

chrome_refuses_exec_script

XSS Detection

 

Fortunately, there are a lot of tools that perform XSS threats scanning so, for the most common issues, there is no need to look at every line of code in every web page when trying to locate such vulnerabilities. One of those tools is OWASP Zed Attack Proxy Object (ZAP). Although it would not be fair to say it is just a XSS scanner, as it provides many many more interesting features.

ZAP can be used as a proxy (indeed, it is based on older Paros Proxy) being able to scan all pages accesed during the session. However, we are just going to introduce the URL( http://……/XSS_Vulnerable/xss_html.jsp?amount=12.25) and press the Attack button (we are using ZAP 2.2.2). To avoid several warnings and making scanning faster we disabled all scan types except for XSS.

ZAP_XSS_vulnerable

Starting from provided URL ( http://……/XSS_Vulnerable/xss_html.jsp?amount=12.25) ZAP has made several checks adding javascript code to be injected.

The tab at the bottom shows one successful attempt of a XSS attack. ZAP has replaced the numeric value of amount parameter by and URL encoded javascript code (as seen in URL field) which is just “><script>alert(1);</script> in plain text (as seen in Evidence field).

Moreover, in the tab above, where HTTP response is shown, the result of the XSS attack is clearly shown: the injected code in amount parameter first closes the double quotes (“) around value for amount field and closes HTML input (>). Afterwards, it adds the script alert(1); (<script>alert(1);</script>). The resulting HTML code to be executed by web browser contains:

<input type="hidden" name="amount" value=""><script>alert(1);</script>" />

XSS Fix

 
Although there is not a single fix for all XSS attacks, all of them are based on input validation, where “input” could be any from HTTP Request parameters, HTTP Headers values or even names… all depends on what the code uses as input.

In our sample app, a HTTP Request parameter is being used to write HTML code.

OWASP provides OWASP Enterprise Security API (ESAPI) in several languages, including, of course Java. ESAPI includes much more functionality related to security, from XSS and CSRF to crypto.

To fix our XSS vulnerability, we are just using a ESAPI encoder (ESAPI 2.1.0). The fix is based on writing the received amount parameter HTML encoded instead of as just received. This way, the user web browser will not execute the javascript code, as it will be seen as the value of the amount parameter.

The fix requires just HTML encoding the amount parameter (see xss_html_esapi.jsp) as follows:

<form method="POST" name="sendForm" id="sendForm" onsubmit="return sendPaymentRequest()">

<%
final String amount = request.getParameter("amount");
Enumeration<String> pNames = request.getParameterNames();
while (pNames.hasMoreElements()){
    final String pName = pNames.nextElement();
    final String pVal = request.getParameter(pName);

    final org.owasp.esapi.Encoder esapiEnc = DefaultEncoder.getInstance();
    final String encPVal = esapiEnc.encodeForHTML(pVal);

    %>
    <input type="hidden" name="<%=pName%>" value="<%=encPVal%>" />
    <%
}
%>

<table>

Running ZAP against fixed JSP (xss_html_esapi.jsp) does not report XSS Vulnerabilities.

XSS: detectar, explotar y corregir vulnerabilidades (II, inyección JS)

En el post anterior vimos cómo inyectar un script en un formulario HTML. También vimos cómo corregir la vulnerabilidad usando la librería ESAPI.

Sin embargo, cómo explotar y corregir la vulnerabilidad depende de cómo se estén usando los parámetros vulnerables. En el ejemplo anterior se usaban para completar código HTML. A continuación veremos una situación en la que el parámetro vulnerable se usa para completar código Javascript.

Seguimos con el mismo ejemplo del atacante que pretende obtener tarjetas de crédito aprovechando una vulnerabilidad en una pasarela de pago. La única diferencia , en este caso, será que la página con el formulario de entrada, en lugar de usar el parámetro amount para completar un input del formulario (HTML), actualiza el valor de dicho campo del formulario mediante Javascript.

A continuación el susodicho JSP:

<%@ 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()">

<table>
<tr>
<td> Cantidad a pagar</td>
<td>
<input type="text" name="amount" maxlength="6" size="6" value="" /> &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>

<script language="javascript"> 
var form = document.getElementById("sendForm"); 
<% final String amount = request.getParameter("amount"); %> 
form = document.getElementById("sendForm"); 
form.amount.value = "<%=amount%>"; 
</script>

</body>
</html>

Se ha resaltado el pequeño código Javascript que actualiza el valor de amount en lugar de completarlo directamente en HTML como se hacía en el post anterior.

Si probamos a inyectar el código anterior, aunque sigue siendo posible, no produce los efectos deseados. El código a inyectar, en este caso, podría intentar dejar el fragmento de código JS tal que así:

<script language="javascript">
var form = document.getElementById("sendForm");
form = document.getElementById("sendForm");

form.amount.value = "23.34" ; 
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>

Produciendo de nuevo el mismo efecto que en el caso HTML, como puede apreciarse:

 

En este caso la solución consistiría en reemplazar la línea:

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

por

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

La única diferencia con respecto a la solución en el caso en que inyectábamos en HTML es que se ha usado el método encodeForJavaScript en lugar de encodeForHTML.

XSS: detectar, explotar y corregir vulnerabilidades

03/24/2012 1 comment

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).

%d bloggers like this: