Exploitation: Conocimientos previos II

Como explicaba en la entrada anterior, la memoria está dividida en 5 segmentos. Para aclarar conceptos vamos a ejecutar el siguiente programa (el código debería ser autoexplicativo).


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

int iglobal_1 = 1;
int global_1;

void fun(int arg_1, int arg_2, int arg_3){
 int local_1 =1, local_2=2;
 // Stack por param
 printf("arg_1 @ 0x%08x con valor %d [STACK]\n",&arg_1,arg_1);
 printf("arg_2 @ 0x%08x con valor %d [STACK]\n",&arg_2,arg_2);
 printf("arg_3 @ 0x%08x con valor %d [STACK]\n\n",&arg_3,arg_3);
 // Stack local
 printf("local_1 @ 0x%08x con valor %d [STACK]\n",&local_1,local_1);
 printf("local_2 @ 0x%08x con valor %d [STACK]\n\n",&local_2,local_2);
}

int main(char* argv[], int argc){
 int local_1;
 char *hvar = malloc(100*sizeof(char));
 strcpy(hvar,"heap-var");

 printf("local_1 @ 0x%08x con valor %d [STACK]\n",&local_1,local_1);
 printf("iglobal_1 @ 0x%08x con valor %d [DATA]\n",&iglobal_1,iglobal_1);
 printf("global_1 @ 0x%08x con valor %d [BSS]\n\n",&global_1,global_1);
 printf("hvar @ 0x%08x con valor %s [HEAP]\n\n",hvar,hvar);

 fun(31,32,33);
 return 0;
}

Lo importante no es tanto el código como el resultado, que permite comprobar el lugar en el que han sido colocadas las variables del programa.

adrian@ubuntu:~/exploiting$ ./a.out
local_1 @ 0xbf850adc con valor 3854324 [STACK]
iglobal_1 @ 0x0804a01c con valor 21 [DATA]
global_1 @ 0x0804a028 con valor 22 [BSS]

hvar @ 0x09043008 con valor heap-var [HEAP]

arg_1 @ 0xbf850ac0 con valor 31 [STACK]
arg_2 @ 0xbf850ac4 con valor 32 [STACK]
arg_3 @ 0xbf850ac8 con valor 33 [STACK]

local_1 @ 0xbf850aac con valor 1 [STACK]
local_2 @ 0xbf850aa8 con valor 2 [STACK]

Observad que el mapa de memoria es el que mostramos en la entrada anterior, los datos en las direcciones más bajas, luego el bss (hay padding de por medio), a continuación las variables del heap y finalmente el stack frame. Fijaos como el stackframe es tal y como comentábamos en la entrada anterior (para 32 bits, en 64 es diferente).

(gdb) x/16wx $sp
0xbffff430:    0x00000000    0x00000000    0xbffff458    0x0028ff80
0xbffff440:    0xbffff488    0x0028ff80    0x00000002    0x00000001
0xbffff450:    0xbffff464    0x00389ff4    0xbffff488    0x080485c5
0xbffff460:    0x0000001f    0x00000020    0x00000021    0xbffff488

Esta muestra de la pila se ha tomado a la entrada de la función fun, tras la inicialización de las variables locales. Fijaos en la dirección de retorno, la 0x080485c5, más arriba está el valor previo del EBP, el 0xbffff488, un poco de padding del compilador y las variables locales con valores 1 y 2. Justo debajo de la dirección de retorno se encuentran los valores que ha recibido como parámetros: 1f (31), 20 (32), 21 (33).

Visto esto, es importante resaltar el tipo de alineación en memoria. El x86 utiliza little endian, lo que quiere decir que el byte menos significativo se coloca en la parte más baja de la memoria. Si queréis podemos ver un ejemplo, usaremos la dirección de retorno, situada en la posición de memoria 0xbffff45c, y la mostraremos como una palabra de 32 bits y luego byte a byte. Como se ve, la palabra está invertida byte a byte.


(gdb) x/wx 0xbffff45c
0xbffff45c:    0x080485c5
(gdb) x/4bx 0xbffff45c
0xbffff45c:    0xc5    0x85    0x04    0x08

Esto quiere decir que si queremos cargar en memoria el valor 0x080485c5, debemos insertarlo como \xc5\x85\x04\x08. Fácil, ¿verdad?.

Otro tema importante cuando hablamos de explotación, es conocer el formato del ejecutable que estamos utilizando. Nos centraremos al principio en Linux, por lo que el formato que nos interesa es el ELF, en las siguientes entradas explicaremos dos clásicos, .plt y .dtors. Cuando hablemos de Windows, el formato que debemos conocer es PE.

Algo importante, tanto para depurar y mostrar direcciones de memoria y valores, como para entender la vulnerabilidad de format strings que explicaremos en futuras entradas (sí, otro clásico, pero esto es un repaso a lo básico, ¿no?), es comprender el funcionamiento de la familia de funciones printf. Veamos qué sucede cuando utilizamos printf de la siguiente manera:


printf("Esto es un número %d\n",20);

(gdb) disas main
Dump of assembler code for function main:
0x080483e4 <main+0>:    push   %ebp
0x080483e5 <main+1>:    mov    %esp,%ebp
0x080483e7 <main+3>:    and    $0xfffffff0,%esp
0x080483ea <main+6>:    sub    $0x10,%esp
0x080483ed <main+9>:    mov    $0x80484d0,%eax
0x080483f2 <main+14>:    movl   $0x14,0x4(%esp)
0x080483fa <main+22>:    mov    %eax,(%esp)
0x080483fd <main+25>:    call   0x804831c <printf@plt>
0x08048402 <main+30>:    mov    $0x0,%eax
0x08048407 <main+35>:    leave
0x08048408 <main+36>:    ret
End of assembler dump.
(gdb) x/s 0x80484d0
0x80484d0:     "Esto es un número %d\n"

Si os fijáis desde main+9 hasta main+25, el compilador ha colocado en la pila la cadena “Esto es un número %d\n”, y también el valor 20 (0x14). La función printf recorre la cadena de texto y va mostrando por pantalla los caracteres, hasta que encuentra un especificador de formato (%d en este caso), y entonces busca en la pila el valor que debe colocar en esa posición. La siguiente tabla muestra los especificadores de formato soportados.

especificadores de formato
especificadores de formato

Merecen especial atención %n, así como %x y -aunque no aparece en la tabla- la posibilidad de imprimir el n-ésimo caracter con el símbolo $.

 


printf("Este es el tercer valor %3$d\n", 1,2,3,4,5);

Imprimirá el siguiente mensaje “Este es el tercer valor 3”. En las siguientes entradas nos metermos ya en los desbordamientos clásicos de buffer, que seguro que es más interesante y entretenido. Como siempre, las dudas o aportaciones, en los comentarios.

 

Anuncios
Tagged with: , , , ,
Publicado en exploiting, hacking
3 comments on “Exploitation: Conocimientos previos II
  1. lodr dice:

    Una cosa, si es little-endian, ¿por qué hemos de cargar 0x080485c5 como \x08\x04\x85\xc5? No sería precisamente al revés. Supongo que lo primero leído irá en la parte más baja de memoria, ¿no?

  2. lodr dice:

    Otra cosa, 0x14 es 20, no 21 (perdón, comento conforme leo)

  3. Adrián dice:

    Typos corregidos, ¡gracias!

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

Archive
A %d blogueros les gusta esto: