#! /bin/sh - setuid - Why is it a security hole?

#! /bin/sh - setuid - Why is it a security hole?

Post by Chris Bitme » Tue, 07 Jun 1994 13:36:08

On those systems that support #! in the kernel and also support setuid

Under what circumstances is this a security hole?


#! /bin/sh - setuid - Why is it a security hole?

Post by Michael Salm » Tue, 07 Jun 1994 16:04:41

|> On those systems that support #! in the kernel and also support
|> setuid scripts:
|> Under what circumstances is this a security hole?

This information comes from the indir package:

Consider a setuid root shell script written in Bourne shell command
language and called `/bin/powerful'.  The first line of the script will
be (without indentation!):


If it doesn't begin with such a line, it's no use to chmod it to 6755
or whatever, because in that case it's just a shell script of the `old'
kind: the Bourne shell receives an exec format error when trying to
execute it, and decides it must be a shell script, so it forks a
subshell with the script name as argument, to indicate from which file
the commands are to be read.  Shell scripts of the `new' kind are
treated as follows: the kernel discovers the magic number `#!' and
tries to execute the command interpreter pointed out, which may be
followed in the script by 1 argument.  Before the exec of the
interpreter the uid and gid fields somewhere in the user structure of
the process are filled in.  Setuid script scheme (kernel manipulations
faked by C routines):

        execl("/bin/powerful", "powerful", (char *) 0);


        setgid(0);      /* possibly */
        execl("/bin/sh", "sh", "/bin/powerful", (char *) 0);

Now, what if the name of the very shell script were e.g. "-i"? Wouldn't
that give a nice exec?

        execl("/bin/sh", "sh", "-i", (char *) 0);

So link the script to a file named "-i", and voila!
Yes, one needs write permission somewhere on the same device, if one's
operating system doesn't support symbolic links.

What about the bare `-' option present in modern versions of the Bourne
shell?  Its goal is to avoid just the thing described above: this
option prevents following arguments of an exec of /bin/sh from being
interpreted as options...

        #!/bin/sh -

        execl("-i", "unimportant", (char *) 0);


        execl("/bin/sh", "sh", "-", "-i", (char *) 0);

And indeed the contents of the file "-i" are executed! However, there's
still another bug hidden, albeit not for long! What if I could `get
between' the setuid()/setgid() and the open() of the command file by
the command interpreter? In that case I could unlink() my link to the
setuid shell script, and quickly link() some other shell script into
its place, couldn't I?


Yet another source of trouble for /bin/sh setuid scripts is the reputed
IFS shell variable. Of course there's also the PATH variable, which
might cause problems. However, one can circumvent these 2 jokers
easily.  A solution to the link()/unlink() problems would be the
specification of the full path of the script in the script itself:

        #!/bin/sh /etc/setuid_script
        shift           # remove the `extra' argument

Some objections:
1)      currently the total length of shell + argument mustn't exceed
        32 chars (easily fixed);
2)      when executing a setuid script, 4.[23]BSD csh is expecting a
        `-b' flag as the first argument, instead of the full path
        (easily fixed);
3)      the interpreter gets an extra argument;
4)      the difficulty of maintaining setuid shell scripts increases -
        if one moves a script, one shouldn't forget to edit it... -
        editing in turn could turn off the setuid bit, so one shouldn't
        forget to chmod(1) the file `back'... - conceptually the
        solution above isn't `elegant'.

How does indir(1) tackle the problems? The script to be executed will
look like:

        #!/bin/indir -u
        #?/bin/sh /etc/setuid_script

Indir(1) will try to open the script and read the `#?' line indicating
the real interpreter and a safe (absolute) pathname of the script. But
remember: the link to the script might have been quickly replaced with
a link to another script, i.e. how can we trust this `#?' line?

Answer: if and only if the file we're reading from is SETUID (setgid)
to the EFFECTIVE uid (gid) of the process, AND it's accessible and
executable for the REAL uid (gid), we know we're executing the original
script (to be 100% correct: the original link might have been replaced
with a link to ANOTHER accessible and executable setuid (setgid) script
of the same owner (group) -> merely a waste of time).

To check the condition stated above reliably, we use fstat(2) on the
file descriptor we're reading from, and stat(2) on the associated file
name.  We compare inode and device numbers to make sure we're talking
about the same file.  Can you figure out why using stat(2) alone would
be insecure?

Feature: we always check if the REAL uid (gid) has access to the setuid
(setgid) script, even if the effective id already differed from the
real id BEFORE the script was executed.  (There isn't even a way to
find out the original effective id.)

If you want the original effective id to be used, you should set the
real id accordingly before executing the script.

To deal with IFS, PATH and other environment problems, indir(1) resets
the environment to a simple default:


When you need e.g. $HOME, you should get it from /etc/passwd instead of
trusting what the environment says. Of course with indir(1) problem 4


Michael Salmon

#include        <standard.disclaimer>
#include        <witty.saying>
#include        <fancy.pseudo.graphics>

Ericsson Telecom AB


#! /bin/sh - setuid - Why is it a security hole?

Post by DEMIG » Thu, 09 Jun 1994 06:32:18

In cshell you can just hit the tab to complete word that the system knows or
that are files in your subdirectory. In Ksh I do this and all I get
is a tab spaceing. How can I have the TAB key complete words for        
me i Ksh. If this is possible please tell me exactly how to do it. Thanks.


Please mail me at


1. Why does /bin/login and /usr/bin/newgrp not have setuid bit set?

Depends on what you mean by _need_.

Unless, of course, it isn't. Usual Unix semantics for /bin/login allow
anyone to call it.

Unless of course you have support for groups with passwords (as does
the shadow suite).

Having gid passwords can also improve security by allowing a user to
change their effective priviledges giving due consideration to how secure
their connection is.

It's a trade-off really. Personnally I've got to trust that the shadow
suite anyhow (or I wouldn't trust /bin/login at all) so I'll keep the
full functionality of newgrp.

 .  _\\__[oo       from       | Phones: +44 121 471 3789 (home)

.  l___\\    /~~) /~~[  /   [ | PGP-fp: D7 03 2A 4B D8 3A 05 37
 # ll  l\\  ~~~~ ~   ~ ~    ~ |         A1 93 FE EA BE E3 2A 91

2. How to get logitech scroll-button to work

3. #!/bin/csh or #!/bin/sh.... why?

4. Version 2.2.5 and 2940

5. why /bin/sh and not /sbin/sh

6. S3 968 Spea Mercury P-64 V: screen is wrapped

7. #!/bin/sh #!/usr/bin/sh can I do both for 2 diff machines

8. Converting MS-Winword-Files to Framemaster-Documents

9. more secure?: "#!/bin/sh -" or "#!/bin/sh"

10. strange behaviour with setuid /bin/sh

11. Security holes in VGA setuid-root utils

12. Security hole if man-2.0a2 installed setuid

13. Security holes, and setuids