Messages in this thread | | | Date | Thu, 30 Dec 1999 19:09:45 -0500 | From | "Theodore Y. Ts'o" <> | Subject | Re: Unexecutable Stack / Buffer Overflow Exploits... |
| |
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/
| |