Bugtraq mailing list archives

Re: StackGuard with ... Re: [Paper] Format


From: "Ronald Huizer [Crew]" <ronald () GALAXY GRAFIX-IS COM>
Date: Mon, 24 Jul 2000 12:28:06 +0200

Hi,

the beginning of Stackguard evading methods have first been discussed
publically in the last Phrack magazine that was published - it described
certain circumstances in which a pointer could be overflown into using a
standard buffer overflow. If user/hacker/cracker supplied data was then
copied to this pointer it was possible to overwrite virtually any memory
area. This method has been used to overwrite EIP on the stack using a
pointer rather than writing over the canary value, thus rendering the
stackguard protection useless. The last version addresses this problem by
keeping track of a table mprotect()ed correctly (ie. NOT PROT_WRITE) which
lists the results of xorring the return address on the stack with a random
canary and barging out if the return address on stack does not equal the
ont in the table.

The problem about this is that an attacker is not only able to modify the
return address on the stack, but virtually ANY memory area in use by the
program. Interesting in this case are: password structures, jmp_buffers,
saved old uid_t's, and lots of other variables on which correct program
flow depends. To truly redirect program flow it's rather easy to write
values to tables kept by glibc in dynamic linked files - The
GLOBAL_OFFSET_TABLE (GOT) is used to dynamically determine at which
address a given function resides - whenever a function in a dynamic linked
file is called, it will be looked up in the GOT, after which it's jumped
to. The mprotection() of the GOT is rather crappy, since it's possible to
modify entries by use of a relocated pointer - we can look up the GOT
entry of a specific function using: objdump --dynamic-reloc <filename> |
grep <function name> | awk '{ print 0x$1 }' or so.

I'll illustrate this with the following example:

#include <stdio.h>

main()
{
   long *GotENT=(long *)0x080494d4;

   *GotENT = 0x41414141;
   printf("Testing\n");

   _exit(0);
}

GotENT contains the address of the GOT entry of the printf() function,
which we found using objdump.
We simply write down 0x41414141 in there and call printf(). The program
gets the address to call to printf() from the GOT and jumps there,
segfaulting our program. Using gdb (or adb, or dbx, whatever you people
like :) we can see that we're in a bit of trouble:

[root@localhost /root]# gdb ./a.out core
GNU gdb 4.17.0.11 with Linux support
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 "i386-redhat-linux"...
Core was generated by `./a.out'.
Program terminated with signal 11, Segmentation fault.
Reading symbols from /lib/libc.so.6...done.
Reading symbols from /lib/ld-linux.so.2...done.
#0  0x41414141 in ?? ()

Since the GOT provides a problem AS SOON AS the offending function is
called, we can use it to evade exit() and _exit() functions, but also as a
measure to evade StackGuard or other mechanisms which use canary values.

The problem with this approach was that the scenario is highly crafted,
and I came across it two times coding some regular exploits. Using format
bugs however, it is possible to write to the GOT every time we want to,
evading any form of protection we'd like - I whipped up a proftpd exploit
(well, lamagra was first, I just used a different technique - Hi Lammy! :)
which succesfully evades StackGuard and openwall (shellcode is on the
heap, which we need to keep PROT_EXEC).

The problem is that there is no "best way approach" to keep stray pointers
from modifying all data structures, but it seems to me that fatal things
such as the global offset table shouldn't be writeable anyway. It's going
to be difficult to provide an all round solution for this problem without
tackling the root of it (ie. dropping %n from libc - or better, making
sure programmers pay attention to what they are doing).

Hope this was usefull to some of you

Ronald Huizer - Scrippie - ronald () grafix nl


Current thread: