Non-executable stack patch

Solar Designer (solar@false.com)
Fri, 6 Jun 1997 06:42:25 -0300 (GMT+3)


Hello!

Well, I guess I should say something on the subject myself, since there's
some misunderstanding of what my patch is for, and what real problems are.

It looks like most people think that buffer overflows are only dangerous in
suid programs. In reality, they're even more dangerous in daemons, and in
different clients such as web browsers.

Now, about getting around non-executable stack in an exploit: yes, this is
possible, and the return-into-libc method is probably the best. However,
note that this requires the knowledge of exact version of libc. Yes, it is
possible to create a generic exploit that will look into libc and find the
offsets -- _for_local_exploits_only_. A simple way to do that for Linux/x86:
(1) search at 0x40000000+ where libc is mmap()ed for the offsets of system()
and "/bin/sh" -- such a string is present for popen(), (2) fill the buffer
with a pattern: function_addr, param1, ..., function_addr, ... and so on --
for the case of system() and "/bin/sh" this means only one parameter, (3)
overflow with such a buffer. If the stack is aligned, it should require up
to two tries only -- until it works. I haven't actually tried that yet, so
there might be some problems that I forgot. A similar approach should work
for Digital UNIX to finally exploit those overflows discussed half a year
ago on BugTraq.

As for remote exploits, it is possible to do the same way, if the attacker
knows the exact version of libc (even the version of the compiler that libc
was compiled with is important). If the daemon is respawning, an attacker
can try offsets for several common versions of libc. Otherwise, he has to
guess from the first try. Also, imagine Netscape got an exploitable buffer
overflow (there should be some), a user checks some web page, his browser
crashes because the web page author didn't guess his version of libc. Will
the user be trying again? How many times? This is to show the patch is still
useful. Of course, it should be stated clearly that the patch is by no means
a complete solution for the buffer overflow problem.

Now, about including the patch in the mainstream kernel. I'm using the patch
myself (what did you expect?) and of course this would be better for me, but
my vote shouldn't be counted since I can't be unbiased. However, in case this
might happen, -- there's one thing about the patch that I don't like myself.
That's running user code at ring 2. It works just fine, and the protection is
still in there -- kernel memory was only protected with segment limits anyway,
not user/supervisor bit on the pages, and I have tested that mprotect() still
works just fine, meaning it doesn't rely on the U/S bit alone also. And still,
it is not the right thing to use ring 2 for user code.

Ring 2 is used to make switching to executable stack a privileged operation,
while still storing all descriptors in the GDT -- this is to prevent from
returning to a RETF instruction (which is a single byte, and is likely to be
present in almost any program), since otherwise this instruction could both
switch to executable stack, and jump to the shellcode.

One of the solutions is to move the user descriptors to LDT (like it is done
in FreeBSD), which seems like the right thing to do regardless of my patch.
This will probably break existing versions dosemu and Wine, but these can be
easily fixed to work again. Or maybe I'm wrong, and it is possible to reserve
some LDT selectors without breaking anything? Well, dosemu will break for
sure since it got the user code and data selectors (0x23 and 0x2B) hardcoded.

Another solution is to allow switching to the executable stack via RETF. This
will still prevent existing overflow exploits, but new remote exploits won't
even have to guess the version of libc, since a RETF can be found in programs
being exploited themselves.

Any other ideas, as well as thoughts on which of those I mentioned is better,
are welcome.

About glibc -- when running glibc 2.0 my patch should simply disable itself.
I was told this is to be fixed in glibc 2.1. Anyway, the patch will not cause
any problems.

Signed,
Solar Designer