domingo, 17 de marzo de 2013

Encontrar vulnerabilidades con ingeniería inversa: IDA PRO + PAIMEI


Hoy vamos a hablar de un método para buscar vulnerabilidades usando ingeniería inversa con IDA PRO y Paimei.Paimei es un framework hecho en python que utiliza python debugger (Pydbg).En lo que nos ayudará Paimei es en ver en que bloques de las funciones del código hemos accedido realizando alguna acción concreta sobre la aplicación.También podemos utilizarlo para fuzzing y ver que cantidad de bloques y funciones hemos asignado y poder así visualizar las zonas de la aplicación que no estamos probando para buscar vulnerabilidades, pero esta parte del fuzzing la veremos en otro momento.

Nota: Para hacer algo parecido podemos usar Binnavi(de pago) o Mynav (gratuito) que ya los veremos mas adelante.

Lo que vamos a hacer en este post es analizar una aplicación de un servidor TFTP.Vamos a realizar alguna acción normal del aplicativo y ver en que bloques y funciones hemos accedido y partiendo de esta base intentar buscar alguna vulnerabilidad.

Lo primero vamos a echar un vistazo al RFC del protocolo TFTP para ver cosas que podemos probar -> http://tools.ietf.org/html/rfc1350


I. Appendix


Order of Headers

                                                  2 bytes
    ----------------------------------------------------------
   |  Local Medium  |  Internet  |  Datagram  |  TFTP Opcode  |
    ----------------------------------------------------------

TFTP Formats

Type   Op #     Format without header

          2 bytes    string   1 byte     string   1 byte
          -----------------------------------------------
RRQ/  | 01/02 |  Filename  |   0  |    Mode    |   0  |
WRQ  -----------------------------------------------
          2 bytes    2 bytes       n bytes
          ---------------------------------
DATA  | 03    |   Block #  |    Data    |
          ---------------------------------
          2 bytes    2 bytes
          -------------------
ACK   | 04    |   Block #  |
          --------------------
          2 bytes  2 bytes        string    1 byte
          ----------------------------------------
ERROR | 05    |  ErrorCode |   ErrMsg   |   0  |
          ----------------------------------------



En este caso, vamos a probar a enviar una peticion de escritura de fichero:

mode = "netascii"
packet = "\x00\x02" + filename + "\x00" + mode + "\x00"

Para instalar paimei hay varias guías por internet -> http://pedramamini.com/PaiMei/docs/installation.html

Y vamos a hacer los siguientes pasos:

Abrimos el binario con IDA y una vez realizo el analisis vamos a File-> Script file y ejecutamos el pida_dump.py :



Aceptamos todo por defecto y guardamos el fichero resultante como TFTPServerSP.exe.pida

A continuación abrimos la consola de paimei -> python PAIMEIconsole.pyw 



Conectamos con nuestra base de datos (en este caso mysql) y nos vamos al Process Stalker de PAIMEI. Nos vamos a Available targets -> boton secundario -> Add target y lo llamamos TFTP.Ahora sobre nuestro target -> boton secundario -> Add Tag , y lo llamamos File Writing, ya que lo que vamos a probar a enviar el comando TFTP de escribir un fichero nuevo.

Ahora sobre nuestro Tag -> boton secundario -> Use for Stalking


Ahora hacemos click en "Add modules" y añadimos nuestro fichero TFTPServerSP.exe.pida generado con IDA anteriormente.Aquí ya podemos ver como nos informa de que hay 476 funciones y unos 2343 bloques.

A continuación vamos a "Refresh Process list" y elegimos nuestro tftpserver que está arrancado (TFTPServerSP.exe). En Coverage blocks seleccionamos "Basic Blocks"  y hacemos click en "Star stalking"

Veremos como el debugger arrancará y ya hará hit en algunos bloques, en este caso se queda parado despues de hacer hit en 22 bloques:



Ahora con el siguiente script en python podemos enviar paquetes para escribir un fichero, concretamente intentamos escribir un fichero con el nombre "AA"

import socket
import sys
host = '127.0.0.1'
port = 69
try:
      s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
       
except:
      print "socket() failed"
      sys.exit(1)
filename = "A"*2
mode = "netascii"
packet = "\x00\x02" + filename + "\x00" + mode + "\x00"
s.sendto(packet, (host, port))


Una vez ejecutamos el script, vemos como hay un total de 297 hits, ademas podemos ver como en el directorio de instalacion del TFTP se ha escrito un fichero AA de tamaño 0.Ahora pinchamos en "Stop Stalking" para que se guarden los datos en la base de datos.

Ahora en nuestro tag "File Writing" -> boton secundario -> Load hits.

Podemos ver el total de funciones y bloques que se han accedido e incluso el estado de los registros del procesador:


Ahora en el tag "File Writing" -> boton secundario -> export to IDA -> Seleccionamos el color que queremos que nos marque los bloques y le damos a "export IDC" y lo guardamos como TFTP.idc

Ahora volvemos a cargar el binario en IDA PRO y una vez termine el analisis vamos a File-> script file -> y ejecutamos el TFTP.idc que hemos generado con Paimei

Podremos ver en el IDA los bloques que se han accedido, en este caso estarán marcados en rojo.

Ahora vamos a intentar buscar alguna vulnerabilidad.Lo primero que vamos a hacer es ver las funciones que importa el binario, nos podemos dar cuenta rápidamente que está hecho en alguna variante de C ya que podemos ver imports a la dll msvcrt , tales como strcpy,memcpy,strcmp , etc.


Si observamos con detalle los imports, podemos observar imports a funciones de ficheros tales como fopen,fwrite,fclose... Tenemos claro que se ha creado un fichero por lo que la función fopen debería haberse utilizado.
Si hacemos doble click en fopen nos traslada al segmento .idata donde están los imports, ahi volvemos a seleccionar fopen y pulsamos "x" para ver las xrefs, es decir, las zonas del binario donde llama a la función fopen:



Si accedemos al primer xrefs , vemos que es un bloque que según PAIMEI no hemos accedido, asi que lo descartamos por ahora(No lo descartamos del todo ya que alguna vez nos podemos encontrar con que PAIMEI no muestra bloques que realmente si han sido accedidos, no es 100% preciso y fiable).


En el segundo xrefs pasa lo mismo, pero en el tercero ya vemos el bloque en rojo, lo que indica que hemos accedido a ese bloque.Podemos partir de ahí.Además podemos ver en el graph overview los bloques que se han accedido, dentro de la función que nos encontramos (sub_404852)



Según podemos ver en el bloque de arriba, vemos que se va a acceder al fichero en modo "rb" , osea en modo "read" y podemos observar que antes llamar a fopen uno de los parametros es el puntero  al  nombre fichero, por lo que vemos como en EAX se guarda [ebp+arg0] y luego se le añade 20h , por lo que ahí tenemos el puntero a nuestra ruta, nos lo apuntamos, y ya lo veremos mas adelante con un debugger.

.text:00404ED3                mov   dword ptr [esp+4], offset aRb ; "rb" accedemos en modo read
.text:00404EDB                mov  eax, [ebp+arg_0]
.text:00404EDE                add   eax, 20h        ; puntero a fichero
.text:00404EE1                 mov  [esp], eax      ; char *
.text:00404EE4                 call    fopen


Después del fopen, en EAX tenemos el retorno devuelto por fopen, y se hace una comparación para ver si existe el fichero , y se hace un Jump en función de si es 0 o no el retorno de la función.

.text:00404EE9                 mov     [ebx+124h], eax

.text:00404EEF                 mov     eax, [ebp+arg_0]
.text:00404EF2                 cmp     dword ptr [eax+124h], 0 -> comparamos el retorno de fopen con 0
.text:00404EF9                 jz         loc_404FE8

Tal y como podemos ver, tenemos en rojo el bloque en el que se ha comprobado que el fichero no existe y en este bloque se comprueba el modo de acceso, en este caso se comprueba si es netascii.



Si es netascii (ese es nuestro caso) se va al siguiente bloque:


En este bloque vemos que se llama a fopen en modo "wt" en escritura, al no existir el fichero, lo creará, y vemos que el puntero al nombre del fichero esta donde ya sabíamos:

mov     eax, [ebp+arg_0]
add     eax, 20h


Hasta ahora no tenemos nada extraño, se comprueba si el fichero existe y si no existe se procede a crear el fichero.En vez de seguir más adelante, vamos a mirar un poco hacia atrás ya que en algún momento se tiene que comprobar el nombre del fichero que estamos intentando escribir.

Si nos vamos un poco más arriba, nos encontramos este bloque interesante:



Donde tenemos un strcpy y un strcat .Podemos comprobar como efectivamente tanto al strcat y al strcpy se les pasa [ebp+arg_0] y luego un add EAX,20h por lo que es el mismo parámetro que se utilizó para el puntero a nombre de fichero del fopen que vimos anteriormente.


mov     eax, [ebp+arg_0]
add      eax, 20h   <- EAX = Puntero a nombre del fichero que se utiliza en fopen
mov     [esp], eax      ; char *
call      strcpy
mov     eax, [ebp+arg_0]
mov     eax, [eax+12Ch]
mov     [esp+4], eax    ; char *
mov     eax, [ebp+arg_0]
add      eax, 20h <- EAX = Puntero a nombre del fichero que se utiliza en fopen
mov     [esp], eax      ; char *
call      strcat

Strcat se utiliza para concatenar y sabemos que uno de los argumentos con los que se llama a la función es el puntero al nombre del fichero.Para saber de una forma y rápida que es lo que se está concatenando podemos utilizar el debugger.

Abrimos el binario con immunity debugger y ponemos un breakpoint en la siguiente dirección, que es cuando se están preparando los argumentos para el strcpy (y posteriormente el strcat)

.text:00404A7D                 mov     dword ptr [esp+4], offset byte_41A570 ; char *

Ya al poner el breakpoint podemos ver realmente que es la constante offset byte_41A570 -> "C:\Archivos de programa\TFTPServer"



Ejecutamos nuestro python script para enviar la petición al servidor tftp y saltará el breakpoint, si vamos avanzando hasta el call strcpy podemos ver como se envía como origen la ruta "C:\Archivos de programa\TFTPServer" y se copia en un buffer destino, en concreto en 006EFB08



A continuación si continuamos con el strcat vemos como origen coge el nombre del fichero que le hemos pasado y como destino el buffer que contiene el  "C:\Archivos de programa\TFTPServer" que se copió anteriormente con el strcpy.Es decir, el nombre del fichero que podemos manipularlo se puede copiar en un buffer que está en la pila y parece que no hay comprobación del tamaño, huele a overflow...


Pero vamos a comprobar si más atrás tenemos alguna comprobación del tamaño del nombre del fichero que le pasamos:



Efectivamente parece que hay alguna comprobación se ve como se comprueba el tamaño de alguna cadena con strlen, y esa cadena tiene pinta de ser el nombre del fichero, en este caso es:

.text:00404934                 mov     eax, [ebp+arg_0]
.text:00404937                 mov     eax, [eax+12Ch]


Y luego vemos como se comprueba el tamaño devuelto por strlen con un valor en EAX,veamoslo en el debugger:

call    strlen
cmp     [ebp+var_C], eax



Y como podemos observar, lo que se comprueba es solo si el tamaño del nombre del fichero que le pasamos es 0 , pero no se comprueba si excede de un tamaño, por lo que.. FAIL !

Vamos a ver que pasa si le pasamos un nombre de fichero con un tamaño muy grande:

import socket
import sys
host = '127.0.0.1'
port = 69
try:
      s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
       
except:
      print "socket() failed"
      sys.exit(1)
filename = "A"*20000   # <- Cambiamos el tamaño del nombre del fichero
mode = "netascii"
packet = "\x00\x02" + filename + "\x00" + mode + "\x00"
s.sendto(packet, (host, port))




Y tal y como podemos ver en el debugger, tenemos un crash, aunque no tenemos control sobre EIP, ya que no sobrescribimos la dirección de retorno en la pila, pero sin embargo vemos algo muy interesante, parece que sobrescribimos la estructura de excepciones (SEH) y ademas podemos ver en el buffer que comentamos anteriormente que se encuentra en la pila como tiene la ruta con el nombre de fichero que le hemos pasado.




Por lo que tenemos un SEH overflow, mas adelante nos escribiremos un exploit para explotar esta vulnerabilidad.

martes, 26 de febrero de 2013

Hacking VoIP - Interceptando llamadas en entornos CISCO


En este post vamos a hablar de una de las cosas mas interesantes y llamativas de hacking en VoIP, las escuchas o interceptaciones de llamadas.

En concreto, vamos a centrarnos en entornos Cisco que están bastante extendidos.Esto es, teléfonos VoIP cisco y centralitas Cisco Call manager. Nos vamos a centrar en este entorno ya que por defecto tienen una directiva de seguridad que impide la interceptación de llamadas.


Vamos a situarnos.Estamos en una auditoría interna (o externa en la que nos hemos hecho con el control de un equipo de la red interna) y nos encontramos un escenario con una red de voz con terminales Cisco y centralitas Call manager:




Lo primero que necesitamos es poder saltar a la VLAN de voz, aquí tenemos 2 opciones en función de como sea el escenario:

Opcion 1 -> Telefonos y PC's comparten switch (bastante habitual).



Esta configuración es muy habitual y se suele implementar por abaratar costes, en este caso, lo barato puede salir caro.Al estar en el mismo switch la red de voz y de datos el saltar a la VLAN de voz es más sencillo.

Simplemente , tenemos que detectar en los mensajes CDP del switch la VLAN de voz, esto es bastante sencillo, si tenemos conectado el pc al teléfono , con wireshark podemos ver estos mensajes.En la siguiente imagen se muestra un ejemplo de un mensaje CDP en el que se muestra la VLAN nativa y la VLAN de voz, en estos mensajes.




Opcion 2 -> Telefonos y pc's en distintos switches. 

Si tenemos acceso físico, podemos probar a conectar nuestro equipo al cable del teléfono y así estaríamos conectados al switch y en la red de voz.Luego ya tendríamos que ver si tenemos que suplantar la MAC
o alguna otra cosa.

Si no, podemos hacerlo, nos queda ver si es posible hacer Vlan hopping por DTP y hacernos pasar por un trunk de un switch. Por ejemplo usando Frogger ( http://www.commonexploits.com/?p=444 ), que tira por debajo de Yersinia.



Una vez tenemos la VLAN de voz (por ejemplo VLAN 200) , tenemos que configurar el interfaz con la VLAN correspondiente , es recomendable hacerlo desde un Linux nativo, desde máquinas virtuales podemos encontrarnos problemas.

modprobe 8021q

vconfig add eth0 200

dhclient eth0.200


Ya estamos en la VLAN de voz, vamos a intentar hacer un arpspoofing entre un teléfono y el gateway.

ettercap -T -M arp:oneway,remote /ip_telefono/ /ip_gateway/


Podemos comprobar que una vez hecho el arp spoofing, podemos ver trafico "Skinny" del telefono al descolgar:





Además, viendo el detalle de este tráfico SKINNY podemos ver información que nos puede valer para más adelante, como el número de extensión, nombre asociado, etc:



Pero a la hora de realizar una llamada, no observamos ningún tráfico RTP, no parece que podamos interceptar llamadas.Por que ?

Bien, aquí viene la parte interesante, estos escenarios de teléfonos Cisco con centralitas Callmanager, tienen un mecanismo de seguridad llamado GARP. En los teléfonos vienen una configuración por defecto de GARP-> Disabled . 

Con esta configuración no será posible interceptar las llamadas aún haciendo arpspoofing, pero no lo harán imposible, en este caso podemos realizar un ataque para cambiar esa configuración en el teléfono y poder interceptar las llamadas. Ucsniff es la solución.


Ucsniff (http://ucsniff.sourceforge.net/usage.html) tiene una funcionalidad que nos permite realizar un ataque TFTP, que consiste en forzar al teléfono en volver a coger la configuración del servidor TFTP correspondiente, una vez ucsniff detecte que ha pedido la configuración y se le ha devuelto el xml de la configuración , parsea el xml y cambia la directiva GARP al vuelo (Con reiniciar el teléfono volverá la configuración anterior al volver a coger la configuración del servidor TFTP legítimo).


Teléfono -> Intenta realizar llamada -> pide configuración por TFTP -> Se intercepta el XML de configuración -> se modifica el valor GARP -> se devuelve al teléfono la configuración modificada.


Ejemplo de configuración:

<vendorConfig> 

<g722CodecSupport>2</g722CodecSupport>

<disableSpeaker>false</disableSpeaker> 

<disableSpeakerAndHeadset>false</disableSpeakerAndHeadset> 

<pcPort>0</pcPort> 

<settingsAccess>1</settingsAccess> 

<garp>0</garp>  <---------------

<voiceVlanAccess>0</voiceVlanAccess> 

<videoCapability>0</videoCapability> 

<autoSelectLineEnable>0</autoSelectLineEnable> 

<webAccess>1</webAccess> 

<daysDisplayNotActive>1,7</daysDisplayNotActive> 

<displayOnTime>08:00</displayOnTime> 

<displayOnDuration>10:30</displayOnDuration> 

<displayIdleTimeout>01:00</displayIdleTimeout> 

<spanToPCPort>1</spanToPCPort> 

</vendorConfig> 

Ahora si que podemos interceptar las llamadas, para ello tenemos que hacer lo siguiente.

En un fichero targets.txt deberemos poner:

IP del teléfono, extensión,nombre persona de la extensión,protocolo (skinny protocol en nuestro caso -> sccp)

En realidad, el nombre no es necesario por lo que podría quedar:

192.168.1.100,443,,sccp

Recordemos que el número de extensión asociado a un nombre lo podemos conseguir observando el tráfico skinny, para así poder elegir nuestro objetivo, un alto cargo de la empresa por ejemplo :)

Una vez creado el targets.txt lanzamos ucsniff con el ataque TFTP especificando el interfaz virtual de la VLAN de voz:

ucsniff -i eth0.200 --tftpm -T -D

Ucsniff utiliza ettercap por debajo , haciendo un MitM entre el teléfono y el gateway, interceptando todas las llamadas entrantes y salientes del teléfono, ya sean entre extensiones internas o incluso llamadas a números moviles/fijos externos.


Además, ucsniff nos dejará en el directorio ficheros .wav con el audio de cada llamada interceptada.Si por algún casual no nos genera el .wav, podemos capturar el tráfico RTP con wireshark y reproducirlo:




En este punto nos podemos encontrar con otro problema.Si el codec de las llamadas es G.722, ni ucsniff ni wireshark seran capaces de reproducir las llamadas por defecto, para ello tenemos que encontrar otra solución:

(tráfico RTP G.722)



Para poder escuchar estas llamadas tenemos varias soluciones:


Poder reproducir en wireshark --> Es preciso compilar wireshark incluyendo un código fuente de una modificación que hicieron la gente de wireshark para tal fin.


Utilizar PCAP2WAV en su modalidad online o offline:

Online (No recomendado) --> http://pcap2wav.xplico.org/ Se trata de un sitio web donde se puede convertir los pcaps para distintos codecs:
(G711ulaw, G711alaw, G722, G729, G723, G726 and RTAudio (x-msrta: Real Time Audio)


Offline --> Si lo queremos tener offline no tenemos más que seguir este hilo:
http://wiki.xplico.org/doku.php?id=pcap2wav



En otro post veremos como podemos solucionar este problema de seguridad en las redes de VoIP.


jueves, 14 de febrero de 2013

WarDriving - Mapeo de redes Wifi


El nombre del articulo lo dice todo (Mapear las redes Wifi mientras conduces con tu vehículo y escribir cruces en los sitios donde se detectan, solo que en nuestro caso lo haremos marcando las coordenadas GPS donde se detectan).

En el siguiente artículo se mostrará cómo realizar un mapa de cobertura Wifi de una zona concreta. Para ello necesitamos:

  • ·         Teléfono móvil con GPS
  • ·         Kismet
  • ·         Giskismet
  • ·         Google earth

Lo primero que debemos hacer es instalar en el teléfono móvil el software que levantará un servicio donde se transmiten las coordenadas GPS.

En nuestro caso como disponemos de un Iphone lo haremos con el software GPS 2 IP para Iphone disponible en el AppStore por 6.99 €. 


En el caso de disponer de un dispositivo Android podríamos hacerlo con el software BlueNMEA disponible en el Market gratuitamente:

















Como se observa en las imágenes anteriores ambas aplicaciones hacen lo mismo, levantar un servicio donde se retransmiten las coordenadas GPS utilizando el protocolo NMEA que es el protocolo universal para GPS.
Por otro lado lo que haremos será compartir la conexión de internet por cable o Wifi es indiferente, ¿el objetivo? Poder llegar por ping y obviamente tener accesible el puerto del servicio concreto, en nuestro caso con el Iphone el puerto es 11123.
Una vez que tenemos compartida la conexión a internet vía tethering y tenemos accesible el teléfono en una máquina Linux haremos:
telnet $(IP_IPHONE) PUERTO | tee /dev/ttys0
Con esto nos estamos conectando al servicio levantado por el iphone y llevándonos a un fichero todas las coordenadas, además de mostrarse por la salida en pantalla.
Una vez que hemos realizado esto ya tenemos un gps montado en /dev/ttys0.
Procedemos ahora a configurar adecuadamente Kismet. Si no lo habéis compilado a mano vosotros y utilizáis la backtrack podemos encontrar la configuración en /usr/local/etc/kismet.conf. En caso contrario podemos buscarlo con find, locate o similares.
En este fichero yo definiré que la interfaz origen de captura siempre será mon0:


Por otro lado indicamos que el gsp es de tipo gpsd y que esta en el /dev/ttys0:


Guardamos el fichero y por ultimo tenemos que ejecutar el siguiente comando:
Gpsd –N –n –D 3 /dev/ttys0
Con esto le indicamos al demonio de gpsd donde tiene que coger los datos del gps.
Posteriormente arrancamos la tarjeta Wifi en modo monitor con:
Airmon-ng start $(nombre_interfaz)
Y arrancamos kismet. En el iphone activamos el gps y automáticamente se estarán guardando las redes Wifi visibles por donde estés pasando.
Posteriormente con giskismet lo guardaremos todo en una base de datos sqlite de la siguiente manera:
Giskismet –x $(fichero).netxml
Finalmente giskismet permite exportar todos los datos a un fichero kml  que será cargado con google earth. La base de datos que crea giskismet tiene la siguiente estructura:

CREATE TABLE clients (
                cid INTEGER PRIMARY KEY AUTOINCREMENT,
                nid INTEGER NULL,
                mac text default NULL,
                manuf text default NULL,
                iptype text default NULL,
                ip text default  NULL);
 CREATE TABLE wireless (
                Id INTEGER PRIMARY KEY AUTOINCREMENT,
                NetworkID INTEGER NULL,
                NetType text default NULL,
                ESSID text default NULL,
                BSSID text default NULL,
                Manuf text default NULL,
                Info text default NULL,
                Channel INTEGER  NULL,
                Cloaked text default NULL,
                Encryption text default NULL,
                Decrypted text default NULL,
                MaxRate INTEGER  NULL,
                MaxSeenRate INTEGER NULL,
                Beacon INTEGER  NULL,
                LLC INTEGER  NULL,
                Data INTEGER  NULL,
                Crypt INTEGER  NULL,
                Weak INTEGER  NULL,
                Total INTEGER  NULL,
                Carrier text default NULL,
                Encoding text default NULL,
                FirstTime text default NULL,
                LastTime text default NULL,
                BestQuality INTEGER  NULL,
                BestSignal INTEGER  NULL,
                BestNoise INTEGER  NULL,
                GPSMinLat float NULL,
                GPSMinLon float NULL,
                GPSMinAlt float NULL,
                GPSMinSpd float NULL,
                GPSMaxLat float NULL,
                GPSMaxLon float NULL,
                GPSMaxAlt float NULL,
                GPSMaxSpd float NULL,
                GPSBestLat float NULL,
                GPSBestLon float NULL,
                GPSBestAlt float NULL,
                DataSize INTEGER NULL,
                IPType text default NULL,
                IP text default  NULL); 


De esta manera podemos realizar consultas a la base de datos sqlite y guardarlas en un fichero kml:
perl giskismet -q "select * from wireless" -o ex1.kml

De esta manera se observan las redes según hemos ido conduciendo donde las wifis se muestran como una diana de color:

  • ·          Rojo --> No dispone de seguridad
  • ·         Naranja --> Seguridad WEP
  • ·         Amarillo --> Seguridad WPA
  • ·         Verde --> Seguridad WPA2