Re: Unexecutable Stack / Buffer Overflow Exploits...

Theodore Y. Ts'o (tytso@MIT.EDU)
Thu, 30 Dec 1999 19:09:45 -0500


From: Steve VanDevender <stevev@efn.org>
Date: Thu, 30 Dec 1999 14:08:21 -0800 (PST)

Exactly how do you propose to make that work? Will the unbounded
str*() and mem*() functions simply have an implicit limit on the
number of characters they copy, or make complex checks to
determine whether copy targets are on the stack and guess how to
bound them to avoid overwriting important parts of the stack
frame? How do you pick the implicit limit that prevents buffer
overruns in an effective number of cases?

Simple; you simply analyze the stack looking for stack frames, and take
some appropriate action (abort, segfault, etc.) if the a string
goes beyond its stack frame.

Sure, this involves a certain amount of hueristics; but so does Solar
Designer's patches --- which don't necessarily work against newer
compilers that use different trampoline formats. At least the layout of
stack frames are relatively static.

The classic stack buffer overrun (smash the stack with code to be
executed on the stack) is always going to be easier to construct
than trickier buffer overruns that try to return into locations
outside the stack, because the stack is mapped at a fixed easily
determined location in all programs, while the location of data
buffers and library routines vary greatly. And since it is
easier to construct, _and_ if systems continue to permit it, then
it will continue to be deployed.

Actually, that's not true. In order for a stack-smash to be developed,
you need to know exactly where in the stack you're going to be, so you
can set the return address appropriately. In general, a particular
exploit only works against a specific binary executable. At MIT Project
Athena, because we used a modified fingerd binary, the stack smash attack
used by the Internet Worm didn't affect us. The return address branched
to the wrong location (since we had extra stuff on the stack) and the
program core dumped instead of executing the attack code.

Hence, simply adding a little bit of code at the beginning of the
program to move the stack by some variable (random) amount is also
sufficient to stop most stack smashing attacks. This approach also has
the advantage of not requiring complicated hueristics to distinguish
"legitimate" trampoline code from illigitimate stack smashes. This code
to randomly bump the stack could also be added to crt1.o, or to
ld-linux.so, which in the latter case would protect executables even
without requiring them to be recompiled.

The only reason why there are so many stack-smash attacks is because
there are a couple of tools written to automatically analyze a
particular binary to produce the exploit. Such tools could easily be
modified by someone with a clue to simulate a valid trampoline code, or
to find some incovenient location in the program to jump into. And,
once those tools exist, we're back to sqaure one, except that there's
more crap (that's now useless) in the kernel.

- Ted

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/