You are on page 1of 21

La Pila de una Funcin en C:

Siguiendo con mi estudio del Exploiting en Linux (gracias al Libro "Linux Exploiting" de
Blackngel), quise ver la evolucin de la pila dentro de una funcin de un programa escrito en C; es
una manera de entender mejor el proceso y lo hice para mi, (necesito ver las cosas en directo) pero
despus pens que podra ser til para los que quieren comenzar en el fabuloso mundo del
exploiting y estn tan verdes como yo ;)
Creo que para empezar hay que tener estas cosas bsicas muy claras y nada mas didctico que
seguir la pila, paso a paso, y ver donde podemos intervenir para obtener una ventaja; que a este
nivel siempre es la misma, llegar a alterar el EIP, registro que indica al procesador la siguiente
direccin que debe ejecutar, de forma que el programa contine por donde nosotros queramos, que
normalmente sera haca un shellcode, para as conseguir ejecutar nuestro cdigo.
Quiero comentar que todo el proceso ha sido realizado en un sistema GNU/Linux, en concreto un
Debian Wheezy de x86_64, sobre un ejecutable compilado para un sistema de 32; y que al ser la
base la arquitectura intel, la informacin creo que es totalmente extrapolable a los sistemas
Windows.

1. Estructura de la Pila en Memoria.


Para empezar vamos a ver como esta organizada la memoria de un programa durante su ejecucin:

En este caso lo que nos interesa es la zona de la pila o Stack, que como vemos es una zona de
memoria que crece de valores de direcciones altas a direcciones bajas y su estructura es del tipo
LIFO, lo ltimo en entrar es lo primero en salir. Siempre se ha comparado con una columna de
platos; pero hay que tener en cuenta que siendo una zona de memoria siempre tenemos nuevos
platos por encima y por debajo y sera los registros ESP y EBP los que delimiten los platos con los
que podemos trabajar. Para complicar un poco el asunto, como ESP apunta a la cima de platos y la
pila crece hacia abajo, tenemos que su direccin siempre sera menor que EBP; como vemos en esta
imagen:

Como analizaremos en nuestro ejemplo, dentro de una funcion() se declara unos valores de EBP y
ESP que sern utilizados solo dentro de la funcin y que se denomina el marco de la pila para dicha
funcin, "stack frame" . EBP siempre estar en una direccin superior a ESP y mientras EBP se
mantiene fijo dentro de la funcin, ESP sera el registro que se mueva cuando se haga uso de la pila,
indicando en cada instante cual es la direccin que corresponde a la cima de la pila.
Muy importante, tened en cuenta que el valor de EBP y ESP son las direcciones de esa zona de
memoria reservada para la pila, no tiene nada que ver con el valor que haya en esa zona de
memoria; como ya veremos ESP es la direccin y DWORD PTR [ESP] esta sealando a los 32
bits alojados en esa zona concreta de memoria indicada por la direccin de ESP.

2. Manejo de la Pila en Ensamblador.


A nivel ensamblador, el registro ESP se ve afectado por cuatro instrucciones:
-PUSH, sirve para subir un valor a la pila, de modo que ESP como tiene que apuntar
a este nuevo valor, disminuye su valor en 4 unidades (Si es un jaleo, pero como crece hacia abajo,
es as....)
-POP, es lo contrario, saca el valor que se encuentra encima de la pila, que esta
sealado por ESP y podemos ver como DWORD PTR[ESP]; al sacarlo de la pila ESP se actualiza
aumentando su valor en 4 unidades. Hablamos de 4 unidades porque estamos en un sistema de 32
donde el registro ESP son 4 bytes.
-CALL, es la instruccin que indica una llamada a una funcin, por tanto como la
ejecucin del programa va a continuar en otra zona, es imprescindible saber donde volver cuando
termine; por tanto se sube a la pila la direccin siguiente a la Call y el valor de ESP se ajusta para
sealar a ese valor.
-RET, es la instruccin complementaria a la anterior, la pila durante la ejecucin de
la funcin se debe encargar (como veremos mas adelante) de que al terminar, la direccin que
hemos guardado antes, se encuentre en la cima de la pila osea esta direccionada por ESP y se realiza
un POP que saca ese valor y el valor de ESP se ajusta. Lo mejor de esta instruccin es que ese valor
sustituye al valor de EIP ( sera como un "pop eip"...) para poder continuar en la siguiente
instruccin despus de la llamada a la funcin; y claro, esto es algo que nos va a ser muy til ;)

3. Un programa en C.
Para este ejercicio me interesaba un programa en C sencillo pero que en su interior se declarase una
funcin, para ello obtuve este cdigo de ejemplo del libro Lenguaje C de Anaya:
Global.c
#include<stdio.h>
voidfuncion();
intcantidad;
main(){
cantidad=10;
funcion();
}
voidfuncion(){
printf("Valordelparametro=%d\n",cantidad);
}

Como veis, es un simple programa para explicar lo que es una variable global, una variable que
definida al principio se puede utilizar en cualquier parte del programa, en este caso la variable
"cantidad" se le da un valor en main() y despus se utiliza en la funcin funcion().
Para compilarlo se utiliza el programa gcc:
gccgm32fnostackprotectorzexecstackGlobal.coGlobal
-g Genera informacin de depuracin
-m32 Como estamos en un sistema de 64 y yo quiero un programa de 32, hay que
indicarlo.
-fno-stack-protector y -z execstack son para evitar que el compilador aada las
protecciones a la pila y nos deje jugar con ella. Adems en este caso me interesa que no se vaya
cambiando la direcciones de la pila en cada reinicio para no volverme mas loco de lo que ya
estoy..........
Una vez compilado tenemos un ejecutable llamado Global, para empezar vamos a ver el cdigo
desensamblado con objdump y en este caso con la opcin -S, que es muy til cuando hemos
compilado con la opcin -g, pues nos mezcla el cdigo original con las instrucciones en
ensamblador:
0804841c<main>:
#include<stdio.h>
voidfuncion();
intcantidad;
main(){
804841c: 55 pushebp
804841d: 89e5 movebp,esp
804841f: 83e4f0 andesp,0xfffffff0
cantidad=10;
8048422: c705c49604080a movDWORDPTRds:0x80496c4,0xa
8048429: 000000
funcion();
804842c: e802000000 call8048433<funcion>
}
8048431: c9 leave
8048432: c3 ret
08048433<funcion>:
voidfuncion(){
8048433: 55 pushebp
8048434: 89e5 movebp,esp
8048436: 83ec18 subesp,0x18
printf("Valordelparametro=%d\n",cantidad);
8048439: a1c4960408 moveax,ds:0x80496c4
804843e: 89442404 movDWORDPTR[esp+0x4],eax
8048442: c70424e0840408 movDWORDPTR[esp],0x80484e0
8048449: e8b2feffff call8048300<printf@plt>
}
804844e: c9 leave
804844f: c3 ret

Solo he puesto la parte que nos interesa, pues sale el desensamblado de todo el ejecutable. Me ha
parecido interesante ver como las instrucciones en C se plasman en el cdigo en ensamblador.
Puede ser muy interesante para reversear cdigo, viendo como se representan estructuras mas
complejas como if....else , for, do...while, switch....case, etc....
Con esta informacin ya estamos preparados para ver su ejecucin y como se va utilizando la pila
durante ese proceso.

4. Siguiendo la Pila.
Para ello vamos a abrir el programa Global con el debugger por defecto de GNU/Linux, gdb, cuya
utilizacin podis repasar en los manuales correspondientes de "Introduccin al Cracking en
Linux":
http://www.4shared.com/dir/37685719/7f21ee72/Cracking_en_Linux.html
Cargamos el programa:
$gdbqGlobal
Y colocamos dos display para que nos mantenga informado de los valores de ESP y EBP y si no lo
tenis colocado en el archivo .gdbinit, debemos colocar la orden para que nos muestre el
desensamblado en formato intel y no en ATT ( odio a ATT, jejeje, no soy capaz de acostumbrarme):
gdb>display$ebp
gdb>display$esp
gdb>setdisassemblyflavorintel
Ahora solo queda colocar un breakpoint en main() y ejecutar el programa con r (run):
gdb>bmain
Breakpoint1at0x8048422:fileGlobal.c,line5.
gdb>r
El programa para aqu:

0x8048422<main+6>: movDWORDPTRds:0x80496c4,0xa
0x804842c<main+16>:
call0x8048433<funcion>
0x8048431<main+21>:
leave
0x8048432<main+22>:
ret
0x8048433<funcion>:
pushebp

A partir de ahora ir colocando primero la instruccin que ya se ha ejecutado y como queda la pila
despus de su ejecucin, con los valores que nos da display tanto para ESP como EBP y la pila en
memoria tal como nos muestra la orden "x/10w $esp" , de forma que veamos grficamente como
se modifica. Para empezar ejecutamos la orden "ni" en gdb para que ejecute la primera instruccin:
movDWORDPTRds:0x80496c4,0xa
2:$esp=(void*)0xffffd450
1:$ebp=(void*)0xffffd458

gdb>x/10w$esp
0xffffd450:
0x08048460 0x00000000 0xffffd4d8 0xf7e6be46
0xffffd460:
0x00000001 0xffffd504 0xffffd50c 0xf7fde860
0xffffd470:
0xf7ff4821 0xffffffff

Para que se vea mas claro la salida de "x/10w $esp" la he colocado en columna, en forma
Direccin: Valor. Como hemos visto antes, en memoria la pila se vera as:
0xffffd474:0xffffffff|
0xffffd470:0xf7ff4821|
0xffffd46c:0xf7fde860|
0xffffd568:0xffffd50c|
0xffffd564:0xffffd504|
0xffffd560:0x00000001|
0xffffd45c:0xf7e6be46v
0xffffd458:0xffffd4d8EBP
0xffffd454:0x00000000
0xffffd450:0x08048460ESP
Pero al final, me ha sido mas fcil colocar la pila de forma clsica con el ESP encima (igual que lo
vemos en Ollydbg) aunque para la numeracin de las direcciones siempre tenemos que tener en
cuenta que la pila crece hacia abajo. Por tanto en este caso la pila la veremos de esta forma:

0xffffd450:0x08048460ESP
0xffffd454:0x00000000
0xffffd458:0xffffd4d8EBP
0xffffd45c:0xf7e6be46
^
0xffffd560:0x00000001
|
0xffffd564:0xffffd504
|
0xffffd568:0xffffd50c
0xffffd46c:0xf7fde860
0xffffd470:0xf7ff4821
0xffffd474:0xffffffff

En este caso hemos cargado en la variable <cantidad> localizada en ds:0x80496c4 el valor 10, en
hexadecimal es 0xa y podemos comprobarlo con gdb:
gdb>x0x80496c4
0x80496c4<cantidad>:

0x0000000a

Continuamos con la orden "si" de gdb, para en este caso seguir la ejecucin del programa entrando
en la funcin():
call0x8048433<funcion>
2:$esp=(void*)0xffffd44c
1:$ebp=(void*)0xffffd458
0xffffd44c:0x08048431
ESP
0xffffd450:0x08048460^
0xffffd454:0x00000000|
0xffffd458:0xffffd4d8EBP

0xffffd45c:0xf7e6be46
0xffffd560:0x00000001
0xffffd564:0xffffd504
0xffffd568:0xffffd50c
0xffffd46c:0xf7fde860
0xffffd470:0xf7ff4821

Se ha ejecutado la instruccin CALL y por tanto se ha subido la direccin de retorno donde


debemos continuar despus de que se ejecute esta llamada, lo marcamos en rojo para seguir su
evolucin. Podemos comprobar que es la direccin correcta con gdb:

gdb>disasmain
Dumpofassemblercodeforfunctionmain:
0x0804841c<+0>:
pushebp
0x0804841d<+1>:
movebp,esp
0x0804841f<+3>:
andesp,0xfffffff0
0x08048422<+6>:
movDWORDPTRds:0x80496c4,0xa
=>0x0804842c<+16>:
call0x8048433<funcion>
0x08048431<+21>:
leave
0x08048432<+22>:
ret
Endofassemblerdump.

Por otro lado, ESP se ha desplazado para sealar este nuevo valor que ha aadido el CALL a la pila,
en este caso ha pasado de valer 0xffffd450 a valer 0xffffd44c y ya sabemos el porque.
Hasta el final, seguimos con la orden "ni" en gdb y as ver las siguientes instrucciones pero sin
entrar en ninguna funcin ms.
pushebp
2:$esp=(void*)0xffffd448
1:$ebp=(void*)0xffffd458
0xffffd448:0xffffd458ESP
0xffffd44c:0x08048431^
0Xffffd450:0x08048460

0xffffd454:0x00000000
0xffffd458:0xffffd4d8EBP
0xffffd45c:0xf7e6be46

0xffffd460:0x00000001

0xffffd464:0xffffd504
0xffffd468:0xffffd50c

0xffffd46c:0xf7fde860
Se ha subido EBP a la cima de la pila, por tanto el valor que seala ESP es la direccin de EBP;
este EBP es la base de la pila de la funcin main() anterior y tambin vamos a marcarlo en azul
para ver su evolucin. Como siempre, ESP ha cambiado para sealar la cima de la pila y pasa a
valer 0xffffd448.
movebp,esp
2:$esp=(void*)0xffffd448
1:$ebp=(void*)0xffffd448

0xffffd448:0xffffd458ESPyEBP
0xffffd44c:0x08048431

0xffffd450:0x08048460

0xffffd454:0x00000000
0xffffd458:0xffffd4d8

0xffffd45c:0xf7e6be46

0xffffd460:0x00000001

0xffffd464:0xffffd504
0xffffd468:0xffffd50c

0xffffd46c:0xf7fde860
Al usar la instruccin "mov destino,origen" el valor de origen sustituye al destino, mientras que el
valor de destino se pierde. Por tanto la direccin de EBP se pierde y acaba valiendo lo mismo que
ESP; por lo tanto en este momento ambos apuntan a la cima de la pla.
subesp,0x18
2:$esp=(void*)0xffffd430
1:$ebp=(void*)0xffffd448
0xffffd430:0x00000001ESP
0xffffd434:0xffffd504
^
0xffffd438:0xffffd50c

FRAMESTACK
0xffffd43c:0xffffd458
0xffffd440:0xf7e847f5

0xffffd444:0xf7fee590

0xffffd448:0xffffd458
EBP
0xffffd44c:0x08048431
0xffffd450:0x08048460

0xffffd454:0x00000000
0xffffd458:0xffffd4d8EBPANTIGUO
0xffffd45c:0xf7e6be46

0xffffd460:0x00000001

0xffffd464:0xffffd504
0xffffd468:0xffffd50c

0xffffd46c:0xf7fde860
Esta instruccin resta un valor hexadecimal de 0x18 al registro ESP, pasando a valer 0xffffd430 ,
como ya sabemos al restar estamos haciendo mas grande la pila y por tanto acabamos de crear el
marco de la pila para la funcion(). Como EBP ha quedado donde antes estaba ESP no podramos
nunca machacar ningn valor del anterior marco de la pila, a no ser que encontremos una
vulnerabilidad que nos permita desbordar el marco de la pila actual y sobre escribir, con los valores
que nos interese, el marco de la pila anterior. En definitiva eso es lo que conseguimos con tcnicas
de exploiting clsicas como el buffer overflow.
Tambin llama la atencin que el valor del actual EBP es el EBP anterior; por lo que me imagino
que se producir una cadena EBP, de tal forma que el valor de EBP ANTIGUO sera el EBP de la
funcin anterior a main(). En gdb, con la orden "frame" nos da toda esta informacin; pero en mi
caso, no habra sabido su significado sin haber estudiado el proceso poco a poco.
gdb>frame
Stacklevel0,frameat0xffffd450:

eip=0x8048439infuncion(Global.c:9);savedeip0x8048431
calledbyframeat0xffffd460
sourcelanguagec.
Arglistat0xffffd448,args:
Localsat0xffffd448,Previousframe'sspis0xffffd450
Savedregisters:
ebpat0xffffd448,eipat0xffffd44c
Noarguments.
Nolocals.

Como vemos los "Saved registers" son justamente las direcciones donde estn los valores que
hemos marcado y vamos a seguir, pues son fundamentales para volver, despus de esta funcin, al
lugar correcto y con la pila adecuada.
moveax,ds:0x80496c4
2:$esp=(void*)0xffffd430
1:$ebp=(void*)0xffffd448
Ponemos en EAX el valor de uno de los parametros que vamos a pasar a la funcin printf, en este
caso el valor de la variable <cantidad>. La pila no se ve afectada y EAX vale 0xa:
movDWORDPTR[esp+0x4],eax
2:$esp=(void*)0xffffd430
1:$ebp=(void*)0xffffd448
Para pasar los parametros a las funciones se suele utilizar la pila, en este caso pasando por EAX,
(pues no se puede hacer directamente) para al final colocarlo en la posicin [esp+4]. En este caso el
tamao de la pila no ha variado, solo hemos machacado el valor anterior indicado por ESP+4 por el
valor de la variable <cantidad>, que en este caso es 0xa.
movDWORDPTR[esp],0x80484e0
2:$esp=(void*)0xffffd430
1:$ebp=(void*)0xffffd448
0xffffd430:0x080484e0ESP
0xffffd434:0x0000000aESP+0x4
0xffffd438:0xffffd50c

0xffffd43c:0xffffd458
0xffffd440:0xf7e847f5

0xffffd444:0xf7fee590

0xffffd448:0xffffd458
EBP
0xffffd44c:0x08048431
0xffffd450:0x08048460

0xffffd454:0x00000000
0xffffd458:0xffffd4d8
0xffffd45c:0xf7e6be46

0xffffd460:0x00000001

0xffffd464:0xffffd504
0xffffd468:0xffffd50c

0xffffd46c:0xf7fde860
Con esta orden tampoco afectamos el tamao de la pila, por lo que ESP no cambia, aunque si

hemos machacado el valor que indicaba ESP con la direccin 0x80484e0:


gdb>x/s0x80484e0
0x80484e0:
"Valordelparametro=%d\n"
Ahora ya estamos preparados para llamar a la funcin printf, con los parmetros necesarios en la
pila. Vamos a aprovechar para recordar que los parmetros de una funcin deben cargarse de
derecha a izquierda, si miramos el cdigo:
printf("Valordelparametro=%d\n",cantidad)
Por eso hemos subido primero la variable <cantidad> a [esp+4] y la cadena "Valor del parametro =
%d\n" en segundo lugar a [esp]
call8048300<printf@plt>
Valordelparametro=10;resultadodeprintf
2:$esp=(void*)0xffffd430
1:$ebp=(void*)0xffffd448
0xffffd430:0x080484e0ESP
0xffffd434:0x0000000aESP+0x4
0xffffd438:0xffffd50c

0xffffd43c:0xffffd458
0xffffd440:0xf7e847f5

0xffffd444:0xf7fee590

0xffffd448:0xffffd458
EBP
0xffffd44c:0x08048431
0xffffd450:0x08048460

0xffffd454:0x00000000
0xffffd458:0xffffd4d8
0xffffd45c:0xf7e6be46

0xffffd460:0x00000001

0xffffd464:0xffffd504
Como vemos con la orden "ni" hemos pasado la CALL sin entrar y por tanto gdb nos da el
resultado mostrado por printf. He mostrado la pila para comprobar que en todas las funciones se
salvaguardan sus valores, y como podemos comprobar son exactamente iguales antes y despus de
la llamada a printf.
leave
2:$esp=(void*)0xffffd44c
1:$ebp=(void*)0xffffd458
La instruccin leave sustituye a dos instrucciones, 1 mov esp, ebp y 2 pop ebp, que restablecen el
marco de la pila anterior, en nuestro caso el de main(), y que nosotros vamos a hacer en dos pasos,
siguiendo las instrucciones clsicas:
1 mov esp,ebp
0xffffd430:0x080484e0
0xffffd434:0x0000000a

0xffffd438:0xffffd50c

0xffffd43c:0xffffd458
0xffffd440:0xf7e847f5

0xffffd444:0xf7fee590

0xffffd448:0xffffd458
EBPyESP
0xffffd44c:0x08048431
0xffffd450:0x08048460

0xffffd454:0x00000000
0xffffd458:0xffffd4d8
0xffffd45c:0xf7e6be46

0xffffd460:0x00000001

0xffffd464:0xffffd504
0xffffd468:0xffffd50c

0xffffd46c:0xf7fde860
La direccin de EBP ha machacado ESP, por tanto los dos valen lo mismo, 0xffffd48.
2 pop ebp
|
0xffffd448:0xffffd458v
0xffffd44c:0x08048431ESP
0xffffd450:0x08048460
0xffffd454:0x00000000
0xffffd458:0xffffd4d8EBP
0xffffd45c:0xf7e6be46

0xffffd560:0x00000001

0xffffd564:0xffffd504

0xffffd568:0xffffd50c
0xffffd46c:0xf7fde860

0xffffd470:0xf7ff4821
Hemos sacado el valor indicado por ESP, 0xffffd458 y hemos machacado EBP; por tanto EBP
vuelve a tener la direccin inicial que le corresponda en main() . Adems ESP se actualiza
disminuyendo el tamao de la pila y por tanto su direccin pasa a ser 0xffffd44c.

ret
2:$esp=(void*)0xffffd450
1:$ebp=(void*)0xffffd458
0xffffd448:0xffffd458|
0xffffd44c:0x08048431
v
0xffffd450:0x08048460ESP
0xffffd454:0x00000000
0xffffd458:0xffffd4d8EBP
0xffffd45c:0xf7e6be46

0xffffd560:0x00000001

0xffffd564:0xffffd504

0xffffd568:0xffffd50c
0xffffd46c:0xf7fde860

0xffffd470:0xf7ff4821

Con ret tomamos el valor de la cima de la pila y sustituimos EIP, de forma que el programa
continuar en 0x8048431. ESP debe actualizarse pues hemos sacado un valor, por lo que pasa a ser
0xffffd450 . Si miramos la direccin de EIP con gdb vemos que todo esta correcto.
gdb>x$eip
=>0x8048431<main+21>: leave
Ya hemos visto, como el marco de la pila main() se ha restablecido totalmente; pero como la misma
funcin main() tambin debe restablecer la pila de la funcin que la llamo, vamos a continuar:
0x08048431<+21>:
leave
2:$esp=(void*)0xffffd45c
1:$ebp=(void*)0xffffd4d8
1 mov esp,ebp
0xffffd448:0xffffd458 |
0xffffd44c:0x08048431
|
0xffffd450:0x08048460 v
0xffffd454:0x00000000
0xffffd458:0xffffd4d8 EBPyESP
0xffffd45c:0xf7e6be46

0xffffd560:0x00000001

0xffffd564:0xffffd504

0xffffd568:0xffffd50c
0xffffd46c:0xf7fde860

0xffffd470:0xf7ff4821
Se iguala la direccin de ESP con respecto a EBP. SI os fijis nuestro valor azul, que empez
siendo el EBP ANTIGUO, ha llegado a ser la direccin de ESP.
2 pop ebp
0xffffd448:0xffffd458
0xffffd44c:0x08048431

0xffffd450:0x08048460
0xffffd454:0x00000000
0xffffd458:0xffffd4d8
0xffffd45c:0xf7e6be46
ESP
0xffffd560:0x00000001

0xffffd564:0xffffd504

0xffffd568:0xffffd50c
0xffffd46c:0xf7fde860

0xffffd470:0xf7ff4821
0xffffd474:0xffffffff

0xffffd478:0xf7ffcff4
0xffffd47c:0x08048254

0xffffd480:0x00000001

0xffffd484:0xffffd4c0

0xffffd488:0xf7fedc16
0xffffd48c:0xf7ffdac0

0xffffd490:0xf7fdeb58

0xffffd494:0xf7fb3ff4

0xffffd498:0x00000000
0xffffd49c:0x00000000

0xffffd4a0:0xffffd4d8

0xffffd4a4:0x67220d99

0xffffd4a8:0x55f6db89
0xffffd4ac:0x00000000

0xffffd4b0:0x00000000

0xffffd4b4:0x00000000

0xffffd4b8:0x00000001
0xffffd4bc:0x08048330

0xffffd4c0:0x00000000

0xffffd4c4:0xf7ff39c0

0xffffd4c8:0xf7e6bd6b
0xffffd4cc:0xf7ffcff4

0xffffd4d0:0x00000001

0xffffd4d4:0x08048330

0xffffd4d8:0x00000000EBP
El valor sealado por ESP se extrae de la pila y machaca EBP, por tanto como sospechbamos los
valores de EBP en diferentes frames se encadenan, por tanto EBP acaba valiendo 0xffffd4d8. Y lo
que es mas importante, ESP se actualiza, reduciendo la pila hasta su valor 0xffffd45c. Ahora la
cima de la pila seala otra direccin de retorno, 0xf7e6be46. Sigo manteniendo nuestro valor azul
0xffffd458, para que comprobis que su valor consecutivo superior 0xffffd45c (EDB+4), nos
seala una direccin con retorno, osea otra forma de llegar a conseguir controlar el EIP.
0x08048432<+22>:
ret
2:$esp=(void*)0xffffd460
1:$ebp=(void*)0xffffd4d8
0xffffd448:0xffffd458
0xffffd44c:0x08048431

0xffffd450:0x08048460
0xffffd454:0x00000000
0xffffd458:0xffffd4d8
0xffffd45c:0xf7e6be46
v
0xffffd560:0x00000001
ESP
0xffffd564:0xffffd504

0xffffd568:0xffffd50c
0xffffd46c:0xf7fde860

0xffffd470:0xf7ff4821
0xffffd474:0xffffffff

0xffffd478:0xf7ffcff4
0xffffd47c:0x08048254

0xffffd480:0x00000001

0xffffd484:0xffffd4c0

0xffffd488:0xf7fedc16
0xffffd48c:0xf7ffdac0

0xffffd490:0xf7fdeb58

0xffffd494:0xf7fb3ff4

0xffffd498:0x00000000

0xffffd49c:0x00000000

0xffffd4a0:0xffffd4d8

0xffffd4a4:0x67220d99

0xffffd4a8:0x55f6db89
0xffffd4ac:0x00000000

0xffffd4b0:0x00000000

0xffffd4b4:0x00000000

0xffffd4b8:0x00000001
0xffffd4bc:0x08048330

0xffffd4c0:0x00000000

0xffffd4c4:0xf7ff39c0

0xffffd4c8:0xf7e6bd6b
0xffffd4cc:0xf7ffcff4

0xffffd4d0:0x00000001

0xffffd4d4:0x08048330

0xffffd4d8:0x00000000EBP
Con ret se extrae el valor de EIP de la pila, por lo que reducimos la pila de forma que ESP es la
direccin 0xffffd560; de este modo hemos terminado la funcin main() y continuamos ya en
libreras del sistema libc con su marco de pila adecuado y en la zona adecuada:
0xf7e6be46<__libc_start_main+230>:
0xf7e6be49<__libc_start_main+233>:

movDWORDPTR[esp],eax
call0xf7e84550<exit>

Como veis, de esta manera salimos del programa llamando a la funcin <exit>.

5. Uso y abuso de la Pila.


Respecto al uso, en general no nos debemos de preocupar porque todo este proceso lo realiza el
compilador por defecto. Eso si el manejo de la pila en ciertos niveles, puede ser muy interesante
para dar una cierta complejidad a nuestro programa, dando una proteccin aadida a nuestro cdigo.
Ahora mismo estaba pensando en los stolen bytes de los antiguos asprotect, que eran ocultados
con diferentes instrucciones, haciendo y deshaciendo mediante la pila, para ocultar el valor correcto
del cdigo que iniciaba el programa ( que tiempos aquellos.....).
Para el exploiting, lo que nos interesa es abusar de la pila para obtener ventajas que en su estructura
original no se haban previsto. La ventaja es siempre la misma, hacernos con el EIP para conseguir
que el programa contine por donde nosotros queramos y ejecute el cdigo que nos interese.
Para empezar necesitamos un programa vulnerable y por tanto el primer responsable es el
programador, que siempre debera tener la seguridad como pensamiento mientras estructura su
programa y nunca olvidar las Prcticas de Programacin Segura. Pero en fin, ya sabemos, el eslabn
mas dbil es el humano y el programador tambin es humano :)
Dentro del programa, debemos buscar una funcin donde se pueda interactuar con el programa; ya
sea porque nos pida datos, pasndole argumentos, pasando variables locales.... sea como sea, la
funcin recibir nuestros datos y los guardara en una zona de la pila que ha preparado para tal fin
llamada buffer y como es lgico estar dentro del marco de la pila de esa funcin. Ese buffer
tendr un tamao prefijado y si el programador, con buena fe, piensa que nunca ningn usuario se
va a poner a introducir AAAAAAAAAAs como un loco, y no comprueba que los valores
introducidos no superen el tamao del buffer, pues tendr un problema bien gordo. Esos problemas

son muchos, pero para empezar vamos a ver varias tcnicas clsicas para sacar provecho:
- Stack Overflows o desborde de la pila. En C suele ser tpico en funciones del tipo
strcpy, strncpy...en general todas las funciones de gestin de cadenas de caracteres o strings. Como
para manejar esas cadenas de caracteres hay que definir un buffer donde almacenarla, es mas
conocido este problema como Buffer Overflow.
Nuestro programa Global no es un buen ejemplo, no interacta con el usuario ni tiene funciones
problemticas pero es una pena desperdiciar todo el trabajo anterior, as que con la ayuda de GDB
vamos a desbordar el marco de la pila y pisar el EIP de retorno para ver como explotar este
problema.
Para empezar nos vamos a colocar pasados la funcin printf en funcion():

0x08048433<+0>:
pushebp
0x08048434<+1>:
movebp,esp
0x08048436<+3>:
subesp,0x18
0x08048439<+6>:
moveax,ds:0x80496c4
0x0804843e<+11>:
movDWORDPTR[esp+0x4],eax
0x08048442<+15>:
movDWORDPTR[esp],0x80484e0
0x08048449<+22>:
call0x8048300<printf@plt>
=>0x0804844e<+27>:
leave
0x0804844f<+28>:
ret

Como vemos por la flecha estamos en 0x804844e, donde la pila se encuentra de esta forma:
0xffffd430:0x080484e0ESP
0xffffd434:0x0000000aESP+0x4
0xffffd438:0xffffd50c

0xffffd43c:0xffffd458
0xffffd440:0xf7e847f5
BUFFER
0xffffd444:0xf7fee590

0xffffd448:0xffffd458
EBP
0xffffd44c:0x08048431 EIP
0xffffd450:0x08048460

0xffffd454:0x00000000
Si suponemos que esta funcin reserva un buffer, lo vamos a colocar de ESP+0x8 hasta
ESP+0x14 pues tanto ESP como ESP+0x4 son los argumentos de printf. Esta nueva funcin
imaginaria y bastante simplona :-P, nos pide que "Introduzca una frase no mas larga de 16 letras",
pues el buffer ocupa 16 bytes; pero deja sin comprobar lo que de verdad hemos introducidos. En
este caso los errores son muchos, el primero la confianza en el usuario medio de que sepa contar
(jajaja), tampoco se valoran los espacios en blanco (es un byte 0x20) y sobretodo, te puedes
encontrar con un rebelde que no admite reglas, y por tanto pone 24 A porque le da la gana ;)
Para representarlo vamos a usar gdb. Primero debes activar la escritura en memoria, mediante la
orden "set write on" y despus ir cambiando todo el buffer por el valor ascii de A en hexadecimal,
que es 0x41
gdb>setwriteon
gdb>set{long}($esp+8)=0x41414141
gdb>set{long}($esp+c)=0x41414141

gdb>set{long}($esp+10)=0x41414141
gdb>set{long}($esp+14)=0x41414141
gdb>set{long}($esp+18)=0x41414141
gdb>set{long}($esp+1c)=0x41414141
Si vemos la pila despus de introducir las 24 A queda as:
0xffffd430:0x080484e0ESP
0xffffd434:0x0000000aESP+0x4
0xffffd438:0x41414141

0xffffd43c:0x41414141
0xffffd440:0x41414141
BUFFER
0xffffd444:0x41414141

0xffffd448:0x41414141
EBP
0xffffd44c:0x41414141 EIP
0xffffd450:0x08048460

0xffffd454:0x00000000
Como vemos las 24A han rebosado el buffer y han seguido machacando zonas a donde nunca
deberan haber llegado, la mas interesante, el valor de EIP que ha subido la instruccin CALL.
Si seguimos con "ni" se ejecutara la instruccin leave dejando la pila asi:
0xffffd440:0x41414141

0xffffd444:0x41414141
0xffffd448:0x41414141v
0xffffd44c:0x41414141ESP
0xffffd450:0x08048460
0xffffd454:0x00000000
0xffffd458:0xffffd4d8EBP
0xffffd45c:0xf7e6be46

0xffffd560:0x00000001
Y por tanto al ejecutarse el ret siguiente, tomar el valor que seala ESP y lo colocar como el
registro EIP, eip:41414141. En este momento vemos que gdb ya nos dice que no tiene acceso a esa
direccin de memoria:
=>0x41414141: Errorwhilerunninghook_stop:
Cannotaccessmemoryataddress0x41414141
0x41414141in??()
Si de todos modos queremos continuar nos dar una excepcin que termina el programa;
gdb>ni
ProgramreceivedsignalSIGSEGV,Segmentationfault.
Una vez conseguida la excepcin y adems comprobamos, por el error, que ha sido por nuestra
accin; ya solo quedara el proceso de crear un exploit que aproveche esta vulnerabilidad. En este
caso sera complicado pues tenemos un buffer ridculo donde tenemos poco espacio para meter
nuestro cdigo; lo importante es que se entienda los peligros que puede provocar un simple buffer
overflow.

- Ataque al Frame Pointer.


El Frame Pointer es otra forma de llamar al registro EBP; que como hemos visto es la direccin
clave para conservar estable dentro de una funcin el marco de la fila y tambin es muy importante
para recuperar el marco de la pila anterior cuando la funcin termine.
Este ataque al frame pointer, puede ser interesante en aquellos casos donde aunque haya desborde
del buffer no podamos llegar al valor EIP y solo podamos acceder al EBP; que como ya hemos
comentado seala a un valor en la pila que es el EBP ANTIGUO y que marcamos en azul para
comprobar su evolucin durante todo el proceso.
Estamos otra vez en la direccin 0x0804844e, cuando se va a ejecutar leave, si vemos la pila en ese
momento:
0xffffd430:0x080484e0ESP
0xffffd434:0x0000000aESP+0x4
0xffffd438:0xffffd50c

0xffffd43c:0xffffd458
0xffffd440:0xf7e847f5
BUFFER
0xffffd444:0xf7fee590

0xffffd448:0xffffd458
EBP
0xffffd44c:0x08048431 EIP
0xffffd450:0x08048460

0xffffd454:0x00000000
En este caso nuestra funcin imaginaria solo nos permite introducir 20 letras espacios incluidos,
pero de nuevo hay un error pues el buffer solo admite 16 (este programador no tiene remedio,jeje).
Con esta premisas, sabemos que podemos desbordar el buffer pero solo llegamos hasta EBP y no
podemos acceder al EIP guardado; por lo que es el valor guardado por nuestro EBP 0xffffd458 el
que nos va a darla solucin. Si miramos el punto 3 y seguimos el valor marcado en azul, podemos
concluir tres cosas :
- 0xffffd458 es el valor del EBP de la funcion anterior, main ()
- Al final de la funcin main() se vuelve a repetir la pareja leave-ret para equilibrar la pila y
si podemos tener una direccin que controlemos en lugar de 0xffffd458, sabemos que este valor va
a acabar siendo ESP:
1 mov esp,ebp
0xffffd448:0xffffd458 |
0xffffd44c:0x08048431
|
0xffffd450:0x08048460 v
0xffffd454:0x00000000
0xffffd458:0xffffd4d8EBPyESP
0xffffd45c:0xf7e6be46EIP
El problema es que con la segunda parte de leave, 2 pop ebp, ESP se ajusta y queda entonces
sealando a un EIP pero con un valor cuatro unidades superiores a la direccin que controlamos:

2 pop ebp
0xffffd454:0x00000000
0xffffd458:0xffffd4d8
0xffffd45c:0xf7e6be46ESPEIP
- Por tanto la direccin que debemos controlar es cuatro unidades por encima de la que
pongamos en lugar del 0xffffd458.
Vamos a hacerlo y lo veremos mejor; seguimos en 0x0804844e
0xffffd430:0x080484e0ESP
0xffffd434:0x0000000aESP+0x4
0xffffd438:0xffffd50c

0xffffd43c:0xffffd458
0xffffd440:0xf7e847f5
BUFFER
0xffffd444:0xf7fee590

0xffffd448:0xffffd458
EBP
0xffffd44c:0x08048431 EIP
Nos hace falta una direccin que controlemos y que podamos modificar; para ello tenemos el
buffer y podemos utilizar la primera, 0xffffd438 que corresponde con ESP+8 y sera donde se
ponga la direccin que queramos sea el EIP , en nuestro caso usaremos 0x41414141. Con gdb
podemos hacerlo fcilmente y lo comprobamos:
gdb>set{long}($esp+8)=0x41414141
gdb>x/10x$esp
0xffffd430:
0x080484e0 0x0000000a 0x41414141 0xffffd458
0xffffd440:
0xf7e847f5 0xf7fee590 0xffffd458 0x08048431
0xffffd450:
0x08048460 0x00000000

Ahora es el momento de atacar el frame point, para ello como es una prueba, vamos a sustituir el
valor guardado en EBP por el valor que controlamos pero quitndole 4 unidades, por tanto seria
0xffffd434:
gdb>set{long}$ebp=0xffffd434
gdb>x/10x$esp
0xffffd430:
0x080484e0 0x0000000a 0x41414141 0xffffd458
0xffffd440:
0xf7e847f5 0xf7fee590 0xffffd434 0x08048431
0xffffd450:
0x08048460 0x00000000

La pila queda asi:


0xffffd430:0x080484e0ESP
0xffffd434:0x0000000aESP+0x4
0xffffd438:0x41414141EIPpara2RET
0xffffd43c:0xffffd458
0xffffd440:0xf7e847f5

0xffffd444:0xf7fee590

0xffffd448:0xffffd434EBP

0xffffd44c:0x08048431EIPpara1RET
Vamos a seguir ejecutando leave con la orden "ni" de modo que veremos como nuestro falso valor
en EBP ya ha funcionado:
0xffffd434:0x0000000aEBP
0xffffd438:0x41414141EIP
0xffffd43c:0xffffd458
0xffffd440:0xf7e847f5
0xffffd444:0xf7fee590
0xffffd448:0xffffd434
0xffffd44c:0x08048431ESP
0xffffd450:0x08048460
Y con ret seguimos pero en la funcin main(), cerrando su marco de pila con leave :

=>0x8048431<main+21>: leave
0x8048432<main+22>: ret

La pila sigue afectada por nuestro cambio y vemos que no es normal, nunca EBP puede estar en
una direccin mas baja que ESP:
0xffffd434:0x0000000a
EBP
0xffffd438:0x41414141
EIP
0xffffd43c:0xffffd458
0xfffffd44:0xf7e847f5
0xffffd444:0xf7fee590
0xffffd448:0xffffd434
0xffffd44c:0x08048431
0xffffd450:0x08048460ESP
Ahora ejecutaremos el leave de main() para completar el ataque. Si ahora miramos la pila veremos
nuestro valor EIP colocado en la cima de la pila, preparado para ser usado por ret:
0xffffd434:0x0000000a
0xffffd438:0x41414141
ESP(EIP)
0xffffd43c:0xffffd458
0xfffffd440:0xf7e847f5
0xffffd444:0xf7fee590
0xffffd448:0xffffd434
0xffffd44c:0x08048431
0xffffd450:0x08048460
Por cierto, EBP ahora es 0x0000000a y claro gdb no lo muestra, da un error. Pero da igual para la
explicacin, si ejecutamos el ltimo ret y miramos los registros:
gdb>inforegisters
eax0x19 0x19
ecx0xffffd418
edx0xf7fb5360

0xffffd418
0xf7fb5360

ebx0xf7fb3ff4
0xf7fb3ff4
esp0xffffd43c
0xffffd43c
ebp0xa 0xa
esi0x0 0x0
edi0x0 0x0
eip0x41414141
0x41414141
eflags0x296
[PFAFSFIF]
Lo hemos conseguido, tenemos el EIP controlado atacando el valor de EBP; si seguimos ya
sabemos lo que va a pasar:
gdb>ni
ProgramreceivedsignalSIGSEGV,Segmentationfault.
- Off-by-One.
No iba a explicar mas tcnicas para abusar de la pila, pero esta tcnica conocida como Offby-one viene perfectamente como continuacin de la anterior.
En este caso nos encontramos con un buffer que no podemos desbordar, pero curiosamente debido a
algn problema hemos permitido que podamos desbordar un solo byte despus del buffer. Por lo
que explica Blackngel en su libro, este problema puede deberse a determinar mal donde poner el
byte final de cadena /x00 o por problemas de calcular la cantidad de bytes, ya sabis no es lo mismo
empezar desde 01 o empezar desde 00.
En fin, sea como sea, tenemos un solo byte que podemos utilizar, y aunque parece poca cosa, en
nuestro caso va a ser mas que suficiente. Para poder utilizar esta tcnica hay que tener en cuenta dos
condiciones:
1 La arquitectura intel de los x86 utiliza el formato little endian para colocar las
direcciones en memoria. Este significa que en memoria una direccin como 0xFFFFD458, esta
colocada al contrario 0x58 0xD4 0xFF 0xFF, de forma que el byte menos significativo se coloca el
primero. Este nos viene de perlas, pues como solo tenemos un byte que alterar ese sera el del final
de la direccin.
Lo podemos ver con gdb, si con la orden x en lugar de que nos muestre los 4 bytes juntos (la
opcin por defecto) le decimos que nos muestre los bytes independientes:
gdb>x$esp
0xffffd44c:

0x08048431

gdb>x/4b$esp
0xffffd44c:
0x31 0x84 0x04 0x08
2 Como consecuencia de lo anterior, las situaciones donde est tcnica nos ser til
son aquellas donde la direccin de EBP y la direccin que vamos a usar falsa tengan los tres
primeros bytes de la direccin iguales. Por eso este caso es perfecto, como tenemos un buffer tan
pequeo la direccin de EBP es 0xFFFFD458 y la que pusimos en su lugar era 0xFFFFD434; por
tanto con solo cambiar el ltimo byte ( vindolo en memoria, por el little endian, es el primero) es
suficiente, siendo el resto de la tcnica exactamente igual al ataque al frame pointer.

Para hacer la prueba con gdb sera igual que el anterior, pero solo vamos a cambiar un byte:
gdb>x$ebp
0xffffd448:

0xffffd458

gdb>setwriteon
gdb>set{char}$ebp=0x34
gdb>x$ebp
0xffffd448:
0xffffd434
gdb>x/4b$ebp
0xffffd448:
0x34 0xd4 0xff 0xff

6. Conclusin:
Como siempre, iba solo a explicar la evolucin de la pila en una funcin y se me ha ido la mano,
jeje, pero bueno la verdad es que tambin quera compartir con todos lo que voy aprendiendo sobre
Exploiting en Linux. Sobretodo espero que haya quedado claro y sirva para todos aquellos que
quieren empezar con el estudio de la explotacin de vulnerabilidades en sistemas Linux.

Cualquier comentario a cvtukan(arroba)gmail.com o en la lista de Crackslatinos.


http://groups.google.com/group/CrackSLatinoS

You might also like