[comp.unix.internals] how to setuid for shell scripts?

K390590%AEARN@pucc.princeton.edu ( Steinparz Franz) (11/15/90)

Could someone give me advice how to make a shell script which inherits
its access rights from its owner as this is done by set uid for regular
programs. Just setting the set uid bit via CHMOD 06xxx does not work
on vax under ultrix.



thanks in advance

Franz Steinparz
K390590@aearn.bitnet

tchrist@convex.COM (Tom Christiansen) (11/16/90)

In article <25009@adm.brl.mil> K390590%AEARN@pucc.princeton.edu ( Steinparz Franz) writes:
>Could someone give me advice how to make a shell script which inherits
>its access rights from its owner as this is done by set uid for regular
>programs. Just setting the set uid bit via CHMOD 06xxx does not work
>on vax under ultrix.

You don't want to do that.  Here follows a longish treatise on why not.

Setuid (and setgid) scripts should be disabled in the kernel.  There's a
lot of confusion about why this is necessary, so this will try to explain
it.  There are the obvious caveats like being careful of IFS and PATH in
both the script and anything it calls, and so on and so forth, but even if
these bases are carefully covered, a subtler problem remains for which no
work-around exists.

When you execute a shell script, what really happens is that the kernel
(at least, modern ones) opens the file you asked it to run, sees the magic
number of "#!", and then calls the interpreter following that to run the
script for you.  The problem is that it passes the name of the script to
the shell, and the shell must then reopen the script.  That means we
opened the script twice.

Consider the following scenario:  /usr/local/bin/zot is a suid shell
script.  ~/snark is a symlink pointing to zot.  ~/gotcha is some
program that if run as root would compromise security, such as by
giving me a root shell.

I type something like "nice +64 snark &" and let the kernel ever so
slowly decide to call the shell with an argument of snark.  It's
already set the euid to root because it's pointing to zot, which is
suid root.  Now the shell ever so slowly starts up.  In the meantime, I
make snark point not to zot but to gotcha.  Bingo!  By the time the
suid shell opens snark, it gets gotcha instead of zot, and the whole
system is down the tubes.

As you see, the presence of ANY suid root script on a system allows anyone
to run anything as root.  This is obviously a Very Bad Thing.  Even a suid
user foo script is bad, due to the transitive nature of insecurity.

Now, if the suid script is in perl or ksh, they could keep working even
with suid scripts disabled in the kernel because these programs have
alternate versions of themselves (suidperl and suid_exec) installed suid
for just this reason.  (Theoretically -- last I checked suid_exec didn't
quite work for this.) Csh and sh scripts, however, simply will not work.

You should really disable suid scripts in your kernel, or get your vendor
to do so if you can't.  This is a strong statement, but from a security
standpoint, it's your only option.  Barring that, seek them out and rendor
them impotent on your system with a find, a file, and a chmod.  You could
actually fix the kernel not to run through namei/lookuppn/foobar twice in
the kernel, but this is harder (less so if you support /dev/fd*).  
Furthermore, other inherent perils remain (see below).

One work around is to create a C wrapper that's suid that does the exec of
the interpreter itself.  You could use the public domain sudo program for
this.

--tom

ps: Here is an additional posting on the subject:

------- Forwarded Message

Date:         10 Aug 90 19:41:32 GMT
From:         vlb@magic.apple.com (Vicki Brown)
Subject:      Re: Suid script security
Organization: Apple Computer
Newsgroups:   comp.unix.questions


In article <14920003@hpdmd48.boi.hp.com> markw@hpdmd48.boi.hp.com (Mark Wolfe) writes:
>
>    I know that suid scripts are a bad idea from reading comp.questions and
>comp.wizards over the last year or so. It seems that just about every guru
>in the world has posted a warning NOT to do it, so I decided I would follow
>the advice (it's a rare subject that all guru's agree on). However, it appears
>that I'm now about to have one of these ugly animals forced on me from above,
>so I'd like some advice:
>
> 1)  Just what are the security risks involved? (i.e. how would someone attack
>     a system via one of these).
>
> 2)  What can I do to make this as secure as possible?

Warning - very long response ahead.  Proceed at your own risk.
There was a very interesting paper in the USENIX Association's publication,
 ;login: ( "How To Write a Setuid Program", Matt Bishop, ;login:
Vol 12, Number 1, January/February 1987).  An excerpt:

    Some versions of UNIX allow command scripts, such as shell scripts,
    to be made setuid ... Unfortunately, given the power and complexity
    of many command interpreters, it is often possible to force them to
    perform actions which were not intended, and which allow the user to
    violate system security.  This leaves the owner of the setuid script
    open to a devastating attack.  In general, such scripts should be avoided.

    ... suppose a site has a setuid script of sh commands.  An attacker
    simply executes the script in such a way that the shell ... appears
    to have been invoked by a person logging in.  UNIX applies the setuid
    bit on the script to the shell, and ... it becomes interactive...

    One way to avoid having a setuid script is to turn off the setuid
    bit on the script, and ... use a setuid [binary] program to invoke it.
    This program should take care to call the command interpreter by its full
    path name, and reset environment information such as file descriptors
    and environment variables to a known state.   However, this method
    should be used only as a last resort and as a temporary measure,
    since with many command interpreters it is possible even under these
    conditions to force them to take undesirable action.

The biggest problem with shell scripts is that you (the programmer /
administrator) have very little control over the programs which run within
the script.  As a very real example, I ran across a script which allowed
users to enter bug reports, using the "vi" editor.  The script was
setuid root, because it wanted to save files in funny places.  The programmer
had guarded against shell escapes (a known feature of vi), by making this
script the login shell.  However, he couldn't guard against another feature
        :e /etc/passwd

You can attempt to make your script as safe as possible by
        1) being very restrictive in your choice of UID.  That is,
           make the script setuid for a non-privileged user, rather than root
           (for example, if it must write a log file, could the log file

           live in some locked area, accessed only by a new and otherwise
           non-privileged account?)
        2) making the script setgid rather than setuid, with a very
           restricted GID (see #1)
        3) ensuring that the script is short, very simple, and does not
           make use of commands such as `vi', `mail' or anything interactive.
           setuid programs should do ONE thing only, and in a non-complex
           manner.
        4) setting the PATH, IFS, and other environment variables explicitly
           within the script
        5) locking down the permissions on the script.  If possible allow it
           to be run only by group members.  Never allow write permission.
        6) If your version of UNIX permits, take away read permission for
           anyone other than the owner.  It's a bit harder to break
           something if you can't see how it works.
        7) Rewrite it in C (carefully)
        8) Convince your management that they don't really need this.

If you plan to keep the script, or re-write it, try and get a copy of the
paper.  If you can't find it, send me mail.
   Vicki Brown   A/UX Development Group         Apple Computer, Inc.
   Internet: vlb@apple.com                      MS 58A, 10440 Bubb Rd.
   UUCP: {sun,amdahl,decwrl}!apple!vlb          Cupertino, CA  95014 USA
              Ooit'n Normaal Mens Ontmoet?  En..., Beviel't?
          (Did you ever meet a normal person?  Did you enjoy it?)

------- End of Forwarded Message

dberg@informix.com (David I. Berg) (11/16/90)

In article <25009@adm.brl.mil> K390590%AEARN@pucc.princeton.edu ( Steinparz Franz) writes:
>Could someone give me advice how to make a shell script which inherits
>its access rights from its owner as this is done by set uid for regular
>programs......

What I've done in the same circumstance is write a one line C program which
system calls the shell script, and then setuid the C program.

valdis@wizards.vt.edu (Valdis Kletnieks) (11/16/90)

In article <25009@adm.brl.mil>, K390590%AEARN@pucc.princeton.edu ( Steinparz Franz) writes:
|> Could someone give me advice how to make a shell script which inherits
|> its access rights from its owner as this is done by set uid for regular
|> programs. Just setting the set uid bit via CHMOD 06xxx does not work
|> on vax under ultrix.

You don't want to do this.  Setuid shell scripts are a Bad Thing.

The security leaks are ENORMOUS - it takes *ANY* user a whole
whopping 3 or 4 commands to get a full-function interactive shell
running under the UID the shell is set-UID to.

I won't give full details, other than to say - how does csh know
to run .login for a login shell, but not a subshell?  Now think
about .login for a while......

(Hint - the shell checks argv[0] for a '-')...

Full details are left as an excersize for the student.

				Valdis Kletnieks
				Computer Systems Engineer
				Virginia Tech

jfh@rpp386.cactus.org (John F. Haugh II) (11/17/90)

In article <633@vtserf.cc.vt.edu> valdis@wizards.vt.edu (Valdis Kletnieks) writes:
>You don't want to do this.  Setuid shell scripts are a Bad Thing.
>
>The security leaks are ENORMOUS - it takes *ANY* user a whole
>whopping 3 or 4 commands to get a full-function interactive shell
>running under the UID the shell is set-UID to.

There are giant holes in the =traditional= method of implementing
setuid shell scripts, this does not mean that there are giant
holes in =every= implementation.

I have, however, yet to be convinced that any vendor has a
reasonable implementation of set-UID shell scripts out there.

The most common reason for vendors continuing to provide
set-UID scripts is that the customers don't understand the
risks well enough to not clamor for the feature.
-- 
John F. Haugh II                             UUCP: ...!cs.utexas.edu!rpp386!jfh
Ma Bell: (512) 832-8832                           Domain: jfh@rpp386.cactus.org
"SCCS, the source motel!  Programs check in and never check out!"
		-- Ken Thompson

guy@auspex.auspex.com (Guy Harris) (11/18/90)

>The security leaks are ENORMOUS - it takes *ANY* user a whole
>whopping 3 or 4 commands to get a full-function interactive shell
>running under the UID the shell is set-UID to.
>
>I won't give full details, other than to say - how does csh know
>to run .login for a login shell, but not a subshell?  Now think
>about .login for a while......
>
>(Hint - the shell checks argv[0] for a '-')...

Yes, but on 4.3BSD and any system that does 4.3BSD-style argument
handling with "#!" (SunOS 3.2 and later, S5R4, probably lots of other
systems), the name of the script does *NOT* get passed to the shell as
"argv[0]", so that *particular* hole isn't there on those systems. 
Dunno if it's fixed in the particular version of Ultrix the original
poster is using, though; unless they know it is, they should assume it
isn't....

There are plenty of other holes.  To close one of them, make sure that
the "#!" line looks like:

	"#! /bin/sh -"		Bourne shell (also Korn and probably
				Bourne-again shells, with appropriate
				change of interpreter path name)

	"#! /bin/csh -b"	4.3BSD and later C shell (and probably
				C-shell derivatives), assuming you've
				written the script in C shell for some
				reason

To close others, make sure you set PATH (or "path", in C shell scripts -
assuming you really *like* writing C shell scripts) before running any
commands, and in Bourne/Korn/Bourne-again shell scripts, set IFS before
setting anything else.

There are probably other things you need to watch out for as well.  The
shells are all big enough programs interpreting big-enough languages
that there are probably other ways of breaking in to
incautiously-written scripts.

On top of that, there's a hole with "#!" that's present in almost all
systems - I think it's fixed in S5R4 and think it'll be fixed in 4.4BSD
- that you *can't* plug except by doing fixes of the sort done in those
systems.

There are alternatives to "#!"; Maarten Litmath has a program whose name
I've forgotten that you can use (he says, in an attempt to prompt
Maarten to follow up :-)).

maart@cs.vu.nl (Maarten Litmaath) (11/20/90)

In article <4432@auspex.auspex.com>,
	guy@auspex.auspex.com (Guy Harris) writes:
)...
)On top of that, there's a hole with "#!" that's present in almost all
)systems - I think it's fixed in S5R4 and think it'll be fixed in 4.4BSD
)- that you *can't* plug except by doing fixes of the sort done in those
)systems.
)
)There are alternatives to "#!"; Maarten Litmath has a program whose name
)I've forgotten that you can use (he says, in an attempt to prompt
)Maarten to follow up :-)).

OK!  :-)
The program is called `indir', because a script starting with
`#!/path/of/indir' is interpreted INDIRectly.  (Does anyone remember V7's
indir(2) system call?)  Note: `indir' is a generalized version of the
`setuid' program; the former can handle non-setuid scripts as well, e.g.
scripts which are hindered by the limited length of a `#!' line.
Example:

	#!/usr/local/bin/indir -u
	#?/bin/sh /safe/path/to/the/setuid/script lots of options
	shell commands

Right.  Tom Christiansen already explained the race condition hole.
Now how about S5R4 and 4.4BSD?  I've been informed that both operating
systems are to launch setuid scripts through the /dev/fd driver.

For example, assume the file `/bin/foo' is a setuid root shell script,
then the kernel handles a system call

	execl("/bin/foo", "foo", "args", (char *) 0)

like this:

	fd = open("/bin/foo", 0);
	fstat(fd, &statbuf);
	if (statbuf.st_mode & S_ISUID)
		seteuid(statbuf.st_uid);
	sprintf(buf, "/dev/fd/%d", fd);
	execl("/bin/sh", "sh", "-", buf, "args", (char *) 0);

Now we're sure the right file gets executed with the right permissions.
However, there appear to be 2 problems with this approach:

1)	`ps' will show something like this -

		  PID TT STAT  TIME COMMAND
		 5393 p1 R     0:00 sh - /dev/fd/3

	- not very informative...

2)	the following construct wouldn't work for setuid sh scripts -

		case $0 in
		foo)
			# the `foo' link was executed
			foo_stuff
			;;
		bar)
			# the `bar' link was executed
			bar_stuff
			;;
		*)
			echo "Unknown service requested.  Abort." >&2
			exit 1
		esac

	- not so nice...

Am I right, Chris?
--
nreadwin@micrognosis.co.uk (Neil Readwin) on "snuff" films:
"Seen one ? I was *in* one. They tortured me horribly and then killed me
 with a single shot in the back of the head ! Following total brain death
 I gave up acting and became a VMS hacker."

brnstnd@kramden.acf.nyu.edu (Dan Bernstein) (11/21/90)

In article <8292@star.cs.vu.nl> maart@cs.vu.nl (Maarten Litmaath) writes:
> 1)	`ps' will show something like this -
> 		  PID TT STAT  TIME COMMAND
> 		 5393 p1 R     0:00 sh - /dev/fd/3

I'd expect the first argument to be mangled into the program name. This
would also solve your second problem.

---Dan