Bugtraq mailing list archives

Re: libresolv+ bug


From: zblaxell () myrus com (Zygo Blaxell)
Date: Thu, 22 Aug 1996 10:52:45 -0400


Quoted from Thomas Ptacek:
1.  A general-purpose setuid program that acts as a switching point for
[ elided ]
2.  An interface script written in a language that handles its own memory
    allocation, like perl, or even GNU flavors of sed and awk; tcl would

Something to consider: I'm guessing that the reason everyone didn't
instantaneously know about the 'suidperl' program, the minute that

Gee, I just guessed that there was probably that sort of problem, and
removed the setuid bits on sight.

the program was brought into the afflicted operating systems, is that
none of the people that normally run through source code finding bugs
wanted to read 'perl'.

I do agree that it should be simpler than perl.  I did say *like* perl.
I forgot to say "but not perl" or "like mini-perl".

It's really hard to do buffer-overrun attacks (or imbedded-0 attacks,
malformed-input attacks, etc) when you have perl-style data types and
regular expressions.  Perl will allow you to imbed nulls in a string,
then do regexp matches against it.  It's easier to validate a perl script
than a C program, because there's much less code to evaluate and more
details are handled by the language.

So, *assuming* that you can make the implementation of the scripting
language secure, you can use it as an algorithmic policy enforcement
agent.  You'd have to start from the ground up designing the thing to
be secure (i.e. it would have to have decent exception handling, robust
string types, array bounds checking, and all that good stuff to make
even trivial programs secure without much thought).  It would have to
be hand-tuned for every OS it was ported to, and it would have to be
able to interact properly with the kernel within the scripting language--
if it didn't, then you would have to validate any external programs called
from the scripting language in their entirety.

If the language is byte-compiled, it can be implemented directly in the
kernel, to boot.  At exec() time, the kernel would spawn a user-mode
interpreter, interpret the privilege rules for the program in question,
then drop the privilege-modification-privilege (unless the p-m-p is one
of the privileges that the exec()ed program must have) and continue the
exec() in normal Unix style.

This is about as general as you can get; unlike authorization vectors
or magic privileged groups, this one lets you make arbitrary decisions
about who gets access to what, without actually modifying the software
that provides the basic functionality.

        - binding to privileged ports (call bind(), drop privileges, exec
                                       child; inndstart does this with innd)

Bad!

We're talking about problems (in this specific instance) in free, open
operating systems, in which anyone can go tinker around in the kernel and
fix whatever it is that concerns them.

Why would you continue forcing programs to run as root, just so they can
do something as trivial as binding to a privileged port (the concept of
privileged ports strikes me as one of the worst concepts ever introduced
into Unix networking code), instead of hacking your kernel so that some
other UID or GID can do it instead (say, anything running as UID "netpriv"
or GID "network"), thus eliminating any real concerns of far-reaching
security programs in a whole ugly host of user-level networking programs?

Well, the point of this structure is that there is exactly one setuid-root
program running as root on the entire machine, which gives away individual
privileges selectively to other programs.  The difference is that in this
model you secure that one program and then you're done; effectively this
is the same as implementing security policy in the kernel, except that
it can be ported to some of the existing Unix systems as well.

If I just wanted to modify the kernel, then I would just put inheritable
authorization vectors in process table entries, with one bit per
privileged function, and then put an authorization vector in the program
text somewhere for setuid^H^H^H^H^H^Howner-privileged programs.  If the
owner's privelege vector is ANDed with the program's privilege vector,
then ORed with the invoking process's privilege vector, then exec()ed
normally.

Of course, it isn't sufficient to assign minimum privileges with
authorization vectors.  A user can do a lot of damage even with just the
privilege to bind to a low port; if I attack some obscure POP client that
uses rcmd(3)-based authorization, and get its bind-to-privileged-port bit,
then I can impersonate anyone I want on the affected machine as far as
rlogin, rsh, etc. are concerned.  I can remove print jobs submitted by
anyone on the machine (or generate them).  I can shut down telnet on the
machine by causing inetd to stop listening to the port, then bind to the
telnet port and impersonate the attacked system.

Certainly the minimum-privileges idea is a good one, but you still can't
have security until all of the privileged programs (no matter how small
their privileges) become robust.

--
Zygo Blaxell. Unix/soft/hardware guru, was for U of Waterloo CS Club, now for
(name withheld by request). 10th place, ACM Intl Collegiate Programming Contest
Finals, 1994.  Admin Linux/TCP/IP for food, clothing, anime.  Pager: 1 (613)
760 8572.  "I gave up $1000 to avoid working on windoze... *sigh*" - Amy Fong



Current thread: