[7570] in bugtraq
Re: A way to prevent buffer overflow exploits? (was: "Any user can
daemon@ATHENA.MIT.EDU (Crispin Cowan)
Fri Aug 7 18:22:17 1998
Date: Fri, 7 Aug 1998 11:21:05 -0700
Reply-To: Crispin Cowan <crispin@CSE.OGI.EDU>
From: Crispin Cowan <crispin@CSE.OGI.EDU>
X-To: Kragen <kragen@pobox.com>
To: BUGTRAQ@NETSPACE.ORG
Kragen wrote:
> On Wed, 5 Aug 1998, Crispin Cowan wrote:
> > <randomizing stack locations>
> > It is also faster than the StackGuard approach, but probably not as effective.
> > In particular, if the attacker uses the technique mentioned above, then they
> > don't have to point the return address at a fixed point within the stack.
> > StackGuard, on the other hand, will detect any buffer overflow that corrupts
> > the return address.
> As has occasionally been pointed out before, buffer overflows don't
> need to overwrite a return address to be dangerous. If you can
> overwrite a pointer to a function, a pointer to a structure containing
> a pointer to a function, etc., you can still execute arbitrary code.
We're working on an extension to StackGuard to address that problem. The idea is
to place canaries next to other data structures that you care about. This is hard,
because you have to mess with the compiler's type system, but we're part-way
through doing it.
> Randomizing stack locations can make all of these a little more difficult.
Depending on the flavor of stack randomization, it might. The original proposal
here is to start the stack at a random location, and otherwise leave it alone.
That's easy to do, but will do absolutely nothing to prevent an overflow that, say,
changes credential information. It mostly makes it hard to aim code pointers at
injected code fragments, because you don't know their absolute address. If you can
inject a BIG overflow, then you can use Aleph's NOP hack to partially overcome that
problem. You can also inject your attack code into a static buffer and aim your
code pointer at that.
There's multiple parts to this problem, and each technique treats one or more parts
of the problem:
* array bounds: if you can prevent buffer overflows, you stop the problem at
the source
* critical data structures: if you can prevent or detect corruption of critical
data structures (i.e. code pointers such as the stack return address, function
pointers, longjmp buffers, etc., and other critical structures such as
credential information, file paths, etc.) then you can prevent the attack from
being exploited
* injectable code: the attacker can inject code into ANY buffer without
overflowing it. If you can make the absolute address of all such buffers hard
to find, then it is hard for the attacker to point code pointers at the buffer
with the attack code
* resident code: the attacker doesn't necessarily have to inject code if the
right code is already resident in the victim program. For instance, if the
program already does "exec(mumble)" somewhere in there, then there is code in
there that calls the exec() system call. The attacker can alter data and then
jump to that resident code.
> The bounds-checking folks are optimistic that bounds-checking can be
> made much faster. ATM, it's 50%-80% overhead in general.
That's interesting. Why do they think they can make such huge strides in the cost
of bounds checking?
I'd be impressed if they could make huge strides in stability, so that a
bounds-checking C compiler could actually compile and run a sophisticated program.
I have been unable to reproduce the BIND experiment where a bounds-checked BIND was
said to run, but very slowly. Our experience with the Jones & Kelly
bounds-checking compiler is that non-trivial programs compiled with it just die. I
would be very happy to be pointed to a bounds-checking compiler that is stable. I
mean no disrespect to Jones & Kelly, as I understand that bounds-checking is
VERY difficult in C, and I'm impressed with how far they got with it.
Crispin