Subject: Re: RFC: migration to a fully dynamically linked system
To: None <tech-userlevel@netbsd.org>
From: Greg A. Woods <woods@weird.com>
List: tech-userlevel
Date: 12/22/2001 01:51:33
[ On Friday, December 21, 2001 at 20:00:06 (-0500), der Mouse wrote: ]
> Subject: Re: RFC: migration to a fully dynamically linked system
>
> > [P]rocesses should not be allowed to change anything in their text
> > areas once they have started executing.
> 
> > (as we know forcing such protection would once and for all and
> > forever prevent any and all buffer-overflow bugs from being effective
> > exploits, at least for executing foreign code, [...])
> 
> I don't think this is true.

I believe it is true, "at least for executing foreign code"  :-)

> Someone (Solar Designer?) wrote a very well-thought-out piece on buffer
> overflows versus non-executable stack segments.

Yes, I've read it....

>  One of the very good
> points made was that preventing executable stacks must means you have
> to find the code segments you want in the executable already - which
> there probably are.  Say you have a running-as-root daemon; you
> overflow something and make it exec /bin/sh.

Yes, this is still a potential danger from a buffer overflow bug.  If an
overflow can trash the return address on the stack then an attacker can
trick a program into running any other code within it, at the "wrong"
time so to speak.  That's one of the things I was hinting at when I
qualified my claim saying it wouldn't prevent overflow bugs from
affecting the integrity of the program and/or the data it operates on.

Run-time checks inserted by the compiler (or linker) can easily block
this attack, BTW, and at very little run-time cost.  If I remember the
reference describing these techniques in more detail I'll post it....

>  Instant root shell - and
> you can do it with "/bin/sh" anywhere (eg, in the overflowed buffer)
> and the exec syscall where it already is, in libc.  If the daemon calls
> exec already, you can do it even with a statically linked daemon.

It's not quite that simple.  You can't do any more than trash the stack.
The likelyhood of being able to trash the stack in such a way that any
existing exec in the text area can be used in a manner under the control
of the remote attacker is very slim.  Make your static strings
unwritable and I think the chances of exploit get even slimmer, at least
for any well written program.  A daemon that execs /bin/sh is not a well
written one.  (wasn't there a bug related to the use of system(3) in
some poorly written fingerd reported the other day on BUGTRAQ?)

> Besides, drawing a line between code and data is difficult at best and
> borderline impossible in some cases.  Shells read scripts into what the
> VM system thinks of as their data segments, but treat them,
> functionally, as code.  perl and Java p-code engines do likewise.

Indeed -- which is why privileged programs should only be written in a
compiled language, following all the long list of rules we've learned
the hard way over the years.  It's also why it's so damn important not
to allow the code segments (i.e. the text areas) of processes
(privileged or otherwise) not ever be allowed to be changed after the
process starts.

Maybe java, smalltalk, mabye even perl, and other similar bytecode
compiled interpreted languages can use similar techniques to protect
blocks of code from change after it's been authenticated and authorised
to run.  Eg. by stuffing the code into an mmap()'ed segment of anonymous
memory and then mprotect()ing it.  Programmers still have to follow all
the rules of writing privileged code, and no doubt these scenarios will
require new rules be discovered and described too.

> Locking down text would raise the bar, but that's all.

That's what I'm interested in doing.  I want to block exploits that
result in foreign code being injected into buggy programs.  We can't
stop people from writing buggy code, and we probably can't find all the
buggy code and fix it before too many people get their hands on it
(though the OpenBSD folks are doing a mighty valiant job trying), so we
can't stop people from running buggy code in situations where it'll make
their systems vulnerable as a result.  However we can do a pretty damn
good job of preventing attacker from taking advantage of these bugs to
inject their own arbitrary foreign code.  This raises the bar far above
where it sits now, above many of the existing exploits, and perhaps even
above the level where scripted attacks can be so brutally effective.  If
our compiler or linker also injected code to save the return address in
a safe place (i.e. no on the stack) and then verify it before every
return instruction then even more vulnerabilities might be mitigated.
Even if these techniques are only used by some people some of the time
they might help discover bugs that could otherwise go unnoticed until
it's too late....

>  It wouldn't
> "once and for all [] prevent any [such exploits]".

You've eliminated the key qualifiers from that quote, again.  :-)

-- 
								Greg A. Woods

+1 416 218-0098;  <gwoods@acm.org>;  <g.a.woods@ieee.org>;  <woods@robohack.ca>
Planix, Inc. <woods@planix.com>; VE3TCP; Secrets of the Weird <woods@weird.com>