[comp.lang.perl] Trouble with setuid

urlichs@smurf.sub.org (Matthias Urlichs) (08/27/90)

It seems that A/UX 2.0(seeding final) doesn't honor the setuid bits when
invoking programs via a script.

This bothers me because Perl wants
#!/usr/local/bin/suidperl
in the first line.

If I say
% suidperl script
then the scruipt runs perfectly well. On the other hand,
% script
(with script of mode 06755) won't do anything except "can't suid" or
something, which here means that geteuid() returns non-zero.

Does anyone know a reasonable workaround?
The only way I've seen is an additional indirection, as in
 #!/bin/sh
 exec /usr/lcoal/bin/suidperl script.real

Not an ideal solution..?
-- 
Matthias Urlichs -- urlichs@smurf.sub.org -- urlichs@smurf.ira.uka.de
Humboldtstrasse 7 - 7500 Karlsruhe 1 - FRG -- +49+721+621127(Voice)/621227(PEP)

tneff@bfmny0.BFM.COM (Tom Neff) (08/27/90)

In article <1990Aug27.181341.425@iwarp.intel.com> merlyn@iwarp.intel.com (Randal Schwartz) writes:
>Good for it.  It's working properly.  Suid scripts are a dangerous
>security hole.  Don't use'em.  If you haven't disabled setuid scripts
>on your system, do that.  Then, put a little C program wrapper around
>your script with the following program ...

Could someone explain to my dimwitted satisfaction how the security
weakness of setuid scripts is corrected by simply exec'ing the scripts
from a setuid wrapper??

-- 
"Don Mattingly is a superstar, so I can justify  \)(/  Tom Neff
that salary." -- George Steinbrenner, after      >()<  tneff@bfmny0.BFM.COM
signing him for $19.6 million over 5 years.      /)(\  uunet!bfmny0!tneff

merlyn@iwarp.intel.com (Randal Schwartz) (08/28/90)

In article <^-i2f2.-42@smurf.sub.org>, urlichs@smurf (Matthias Urlichs) writes:
| It seems that A/UX 2.0(seeding final) doesn't honor the setuid bits when
| invoking programs via a script.

Good for it.  It's working properly.  Suid scripts are a dangerous
security hole.  Don't use'em.  If you haven't disabled setuid scripts
on your system, do that.  Then, put a little C program wrapper around
your script with the following program (thanks Larry) (by the way,
this program is its own manpage in the format that Larry developed...
if you don't have pl28 running, throw away anything that doesn't look
like a Perl program before execution):

================================================== snip here
#!/usr/bin/perl
'di';
'ig00';
#
# $Header$
#
# $Log$

if ($#ARGV >= 0) {
    @list = @ARGV;
    foreach $name (@ARGV) {
	die "You must use absolute pathnames.\n" unless $name =~ m|^/|;
    }
}
else {
    open(DF,"/etc/mount|") || die "Can't run /etc/mount";

    while (<DF>) {
	chop;
	$_ .= <DF> if length($_) < 50;
	@ary = split;
	push(@list,$ary[2]) if ($ary[0] =~ m|^/dev|);
    }
}
$fslist = join(' ',@list);

die "Can't find local filesystems" unless $fslist;

open(FIND,
  "find $fslist -xdev -type f \\( -perm -04000 -o -perm -02000 \\) -print|");

while (<FIND>) {
    chop;
    next unless -T;
    print "Fixing ", $_, "\n";
    ($dir,$file) = m|(.*)/(.*)|;
    chdir $dir || die "Can't chdir to $dir";
    ($dev,$ino,$mode,$nlink,$uid,$gid,$rdev,$size,$atime,$mtime,$ctime,
       $blksize,$blocks) = stat($file);
       die "Can't stat $_" unless $ino;
    chmod $mode & 01777, $file;		# wipe out set[ug]id bits
    rename($file,".$file");
    open(C,">.tmp$$.c") || die "Can't write C program for $_";
    $real = "$dir/.$file";
    print C '
main(argc,argv)
int argc;
char **argv;
{
    execv("' . $real . '",argv);
}
';
    close C;
    system '/bin/cc', ".tmp$$.c", '-o', $file;
    die "Can't compile new $_" if $?;
    chmod $mode, $file;
    chown $uid, $gid, $file;
    unlink ".tmp$$.c";
    chdir '/';
}
##############################################################################

	# These next few lines are legal in both Perl and nroff.

.00;			# finish .ig
 
'di			\" finish diversion--previous line must be blank
.nr nl 0-1		\" fake up transition to first page again
.nr % 0			\" start at page 1
';__END__ ############# From here on it's a standard manual page ############
.TH SUIDSCRIPT 1 "July 30, 1990"
.AT 3
.SH NAME
suidscript \- puts a compiled C wrapper around a setuid or setgid script
.SH SYNOPSIS
.B suidscript [dirlist]
.SH DESCRIPTION
.I Suidscript
creates a small C program to execute a script with setuid or setgid privileges
without having to set the setuid or setgid bit on the script, which is
a security problem on many machines.
Specify the list of directories or files that you wish to process.
The names must be absolute pathnames.
With no arguments it will attempt to process all the local directories
for this machine.
The scripts to be processed must have the setuid or setgid bit set.
The suidscript program will delete the bits and set them on the wrapper.
.PP
Non-superusers may only process their own files.
.SH ENVIRONMENT
No environment varialbes are used.
.SH FILES
None
.SH AUTHOR
Larry Wall
.SH "SEE ALSO"
.SH DIAGNOSTICS
.SH BUGS
.ex
================================================== snip here

eval unpack("u","A<')I;G0@)TIU<W0@86YO=&AE<B!097)L(&AA8VME<BPG")
-- 
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
| merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Welcome to Portland, Oregon, home of the California Raisins!"=/

merlyn@iwarp.intel.com (Randal Schwartz) (08/28/90)

In article <15805@bfmny0.BFM.COM>, tneff@bfmny0 (Tom Neff) writes:
| In article <1990Aug27.181341.425@iwarp.intel.com> merlyn@iwarp.intel.com (Randal Schwartz) writes:
| >Good for it.  It's working properly.  Suid scripts are a dangerous
| >security hole.  Don't use'em.  If you haven't disabled setuid scripts
| >on your system, do that.  Then, put a little C program wrapper around
| >your script with the following program ...
| 
| Could someone explain to my dimwitted satisfaction how the security
| weakness of setuid scripts is corrected by simply exec'ing the scripts
| from a setuid wrapper??

To do so would reveal the whole hole, but hey, what the heck, I heard
about it in comp.unix.wizards anyway, so here goes...

Theres a small but non-zero window of time between when the system
code handling execve() notices the setuid bits and when the file is
opened by the shell selected from the #! magic number.

During that window, renaming the file (or a link to it... there's a
hint) can cause one pile-o-text to be subbed for the other
pile-o-text.  The first is the "real" script, and the second is your
arbitrary program.  The net effect is that your abitrary program is
executed with the privileges of the "real" script.

How big is the window?  Big enough to get at from a *shell* program
invoking "rm" and "mv" and "ln" (I know, I wrote one)!  Sheesh.

The wrapper helps because the file executed by the wrapper is
explictly named, and short of being able to move *that* file around,
you're pretty safe.

Just another security weenie and Perl hacker,
-- 
/=Randal L. Schwartz, Stonehenge Consulting Services (503)777-0095 ==========\
| on contract to Intel's iWarp project, Beaverton, Oregon, USA, Sol III      |
| merlyn@iwarp.intel.com ...!any-MX-mailer-like-uunet!iwarp.intel.com!merlyn |
\=Cute Quote: "Welcome to Portland, Oregon, home of the California Raisins!"=/

urlichs@smurf.sub.org (Matthias Urlichs) (08/28/90)

In comp.lang.perl, article <1990Aug27.181341.425@iwarp.intel.com>,
  merlyn@iwarp.intel.com (Randal Schwartz) writes:
< In article <^-i2f2.-42@smurf.sub.org>, urlichs@smurf (Matthias Urlichs) writes:
< | It seems that A/UX 2.0(seeding final) doesn't honor the setuid bits when
< | invoking programs via a script.
< 
< Good for it.  It's working properly.  Suid scripts are a dangerous
< security hole.  Don't use'em.

Sorry, Randal, but I was not talking about the setuid bits of the script.
(If that wasn't obvious, sorry.)
What I have trouble with are the setuid bits of the _program_ (suidperl, in
that case), which are also not honored.

In that case, there's no security hole, since suidperl will first open the
script and then use fstat() to find out about the setuid bits.

-- 
Matthias Urlichs -- urlichs@smurf.sub.org -- urlichs@smurf.ira.uka.de
Humboldtstrasse 7 - 7500 Karlsruhe 1 - FRG -- +49+721+621127(Voice)/621227(PEP)

pvo@sapphire.OCE.ORST.EDU (Paul O'Neill) (08/29/90)

In article <1990Aug27.181341.425@iwarp.intel.com> merlyn@iwarp.intel.com (Randal Schwartz) writes:
>
>Good for it.  It's working properly.  Suid scripts are a dangerous
>security hole.  Don't use'em.  If you haven't disabled setuid scripts
>on your system, do that.  Then, put a little C program wrapper around
>your script with the following program 
>

Hmmmm.... Who's the author of this new book? :-)

Preferably you disable setuid scripts and use perl setuid emulation.
As a second choice, gift-wrap in C.
You don't do both.

From the man page:

	Setuid  perl scripts are safer than C programs
     through a dataflow tracing  mechanism  which  prevents  many
     stupid  security  holes...............

     In an unpatched 4.2 or 4.3bsd  kernel,  setuid  scripts  are
     intrinsically  insecure, but this kernel feature can be dis-
     abled.  If it is, perl can emulate  the  setuid  and  setgid
     mechanism  when  it notices the otherwise useless setuid/gid
     bits on perl scripts. 

	[ie-perl AUTOMAGICALLY invokes suidperl on a setuid perl script
	 IF setuid scripts are disabled.]

                            If the kernel feature isn't disabled,
     perl  will  complain  loudly  that  your  setuid  script  is
     insecure.  You'll need to either disable the  kernel  setuid
     script feature, or put a C wrapper around the script.

	[OR, you can cheat, and MANUALLY invoke suidperl.  Larry says 
	all bets are off on the security of this approach.]

	Does your system have setreuid()?  You *MAY* want to cheat this
	way.

-----------------From a Larry Wall comp.lang.perl posting:---------------

: I found a great use for suidperl.  I invoke it directly on my suid perl 
: script, or start the suid perl script with the line:
: #!/usr/local/bin/suidperl
: 
: And the loud complaining about the kernel patch goes away!!
: (Now I get to see all the loud complaining about insecure paths, etc.,
: neat stuff.)  Is this a nasty that should be fixed?

Possibly.  It is certainly the case that if you put a setuid root csh or sh
script on your system, and give me a normal account on your system, I could
have root privs within about 2 minutes of the time I find the script.

: If so, why?  Am I asking for trouble doing this w/o the kernel patches?

Possibly, especially if you machine doesn't support setreuid().  The problem
is that, because of the aliasing possible with links, there is no way to
guarantee that the script opened by the interpreter is in fact the same
one that the kernel examined and granted setuid privs to.  

Now suidperl goes to great lengths to assure that the script you are
executing is a set-id script, and that the arguments passed on the command
line correspond to the #! line, but it can't assure that it is the same
script that the kernel execed.  If you have setreuid(), suidperl can
swap uid and euid and check that it isn't giving away any privs that
the kernel wouldn't give away.  If you don't, perl can only use access
to check the directory permissions, which is known to have race conditions.
(Though perl uses fstat to test the file permissions itself.)  So suidperl
won't let you run a script that's protected against you, and it won't
let you run set-id to anything other than what the script itself is set
to, but it can let you run a script in a directory that you don't have
access to ordinarily, if you don't have setreuid().

A worse problem is if you decide to bypass the loud message by invoking
taintperl rather than suidperl.  This also bypasses the checks, because
taintperl will assume that it is running a wrapped script.  In this case,
a setuid perl script is as bad as a setuid shell script--basically gives
away the farm.

A factor contributing to this is that if setuid is really working, and the
uid of the script is not root, then the script uid overrides the suidperl
uid, and suidperl will note that it is not running as root, and exit.  So
your trick only works on setuid root programs.  This might tempt you on 
non-root setuid scripts to use taintperl instead, but that's a losing
solution, as pointed out in the previous paragraph.
 
: Can I still 
: consider my "perl script ... more secure than the corresponding C program"?

Maybe.  If you have setreuid(), it probably is.  But I'm not going to
guarantee it if you haven't disabled set-id scripts in the kernel.

-------------------------END OF L.WALL QUOTE-------------------


Paul O'Neill                 pvo@oce.orst.edu		DoD 000006
Coastal Imaging Lab
OSU--Oceanography
Corvallis, OR  97331         503-737-3251

worley@compass.com (Dale Worley) (08/29/90)

   X-Name: Matthias Urlichs

   In that case, there's no security hole, since suidperl will first open the
   script and then use fstat() to find out about the setuid bits.

Wouldn't it be possible to put this check into the shell, so setuid
shell scripts would be OK?

And doesn't suidperl have to also verify that the user is allowed to
execute this file, as well as checking the setuid bit?

Dale Worley		Compass, Inc.			worley@compass.com
--
The illicit drug business has been described--not entirely in jest--as
the best means ever devised by the United States for exporting the
capitalist ethic to potentially revolutionary Third World peasants.