markw@hpdmd48.boi.hp.com (Mark Wolfe) (08/09/90)
I know that suid scripts are a bad idea from reading comp.questions and comp.wizards over the last year or so. It seems that just about every guru in the world has posted a warning NOT to do it, so I decided I would follow the advice (it's a rare subject that all guru's agree on). However, it appears that I'm now about to have one of these ugly animals forced on me from above, so I'd like some advice: 1) Just what are the security risks involved? (i.e. how would someone attack a system via one of these). 2) What can I do to make this as secure as possible? I know these questions have been asked and answered before on the net, but I didn't save the notes because as I said before, I'd just planned never to do it. Please help. If the answers are too sensitive (especially for no. 1), please email your answers. markw@hpbs1529.boi.hp.com Mark Always remember: where ever you go...there you are. - Buckaroo Bonzai
maart@cs.vu.nl (Maarten Litmaath) (08/10/90)
In article <14920003@hpdmd48.boi.hp.com>, markw@hpdmd48.boi.hp.com (Mark Wolfe) writes: )... ) 1) Just what are the security risks involved? (i.e. how would someone attack ) a system via one of these). See below. ) 2) What can I do to make this as secure as possible? Convert it to a C program or use the indir(1) package I wrote (or something equivalent). It's in one of the latest volumes in comp.sources.unix. ------------------------------------------------------------------------------- Setuid Shell Scripts -------------------- how to get them safe Maarten Litmaath (maart@cs.vu.nl) 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!): #!/bin/sh 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); | V setuid(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 csh command interpreter? Well, 4.2BSD provides us with a csh which has a NEW option: "-b"! Its goal is to avoid just the thing described above: the mnemonic for `b' is `break'; this option prevents following arguments of an exec of /bin/csh from being interpreted as options... The csh refuses to run a setuid shell script unless the option is present... Scheme: #!/bin/csh -b ... execl("-i", "unimportant", (char *) 0); | V setuid(0); setgid(0); execl("/bin/csh", "csh", "-b", "-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? Right. 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) 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 bits, 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: PATH=/bin:/usr/bin:/usr/ucb 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 remains. -------- -- "UNIX was never designed to keep people from doing stupid things, because that policy would also keep them from doing clever things." (Doug Gwyn)
vlb@magic.apple.com (Vicki Brown) (08/11/90)
In article <14920003@hpdmd48.boi.hp.com> markw@hpdmd48.boi.hp.com (Mark Wolfe) writes: > > I know that suid scripts are a bad idea from reading comp.questions and >comp.wizards over the last year or so. It seems that just about every guru >in the world has posted a warning NOT to do it, so I decided I would follow >the advice (it's a rare subject that all guru's agree on). However, it appears >that I'm now about to have one of these ugly animals forced on me from above, >so I'd like some advice: > > 1) Just what are the security risks involved? (i.e. how would someone attack > a system via one of these). > > 2) What can I do to make this as secure as possible? Warning - very long response ahead. Proceed at your own risk. There was a very interesting paper in the USENIX Association's publication, ;login: ( "How To Write a Setuid Program", Matt Bishop, ;login: Vol 12, Number 1, January/February 1987). An excerpt: Some versions of UNIX allow command scripts, such as shell scripts, to be made setuid ... Unfortunately, given the power and complexity of many command interpreters, it is often possible to force them to perform actions which were not intended, and which allow the user to violate system security. This leaves the owner of the setuid script open to a devastating attack. In general, such scripts should be avoided. ... suppose a site has a setuid script of sh commands. An attacker simply executes the script in such a way that the shell ... appears to have been invoked by a person logging in. UNIX applies the setuid bit on the script to the shell, and ... it becomes interactive... One way to avoid having a setuid script is to turn off the setuid bit on the script, and ... use a setuid [binary] program to invoke it. This program should take care to call the command interpreter by its full path name, and reset environment information such as file descriptors and environment variables to a known state. However, this method should be used only as a last resort and as a temporary measure, since with many command interpreters it is possible even under these conditions to force them to take undesirable action. The biggest problem with shell scripts is that you (the programmer / administrator) have very little control over the programs which run within the script. As a very real example, I ran across a script which allowed users to enter bug reports, using the "vi" editor. The script was setuid root, because it wanted to save files in funny places. The programmer had guarded against shell escapes (a known feature of vi), by making this script the login shell. However, he couldn't guard against another feature :e /etc/passwd You can attempt to make your script as safe as possible by 1) being very restrictive in your choice of UID. That is, make the script setuid for a non-privileged user, rather than root (for example, if it must write a log file, could the log file live in some locked area, accessed only by a new and otherwise non-privileged account?) 2) making the script setgid rather than setuid, with a very restricted GID (see #1) 3) ensuring that the script is short, very simple, and does not make use of commands such as `vi', `mail' or anything interactive. setuid programs should do ONE thing only, and in a non-complex manner. 4) setting the PATH, IFS, and other environment variables explicitly within the script 5) locking down the permissions on the script. If possible allow it to be run only by group members. Never allow write permission. 6) If your version of UNIX permits, take away read permission for anyone other than the owner. It's a bit harder to break something if you can't see how it works. 7) Rewrite it in C (carefully) 8) Convince your management that they don't really need this. If you plan to keep the script, or re-write it, try and get a copy of the paper. If you can't find it, send me mail. Vicki Brown A/UX Development Group Apple Computer, Inc. Internet: vlb@apple.com MS 58A, 10440 Bubb Rd. UUCP: {sun,amdahl,decwrl}!apple!vlb Cupertino, CA 95014 USA Ooit'n Normaal Mens Ontmoet? En..., Beviel't? (Did you ever meet a normal person? Did you enjoy it?)
guy@auspex.auspex.com (Guy Harris) (08/12/90)
>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 csh command interpreter? Well, 4.2BSD provides us with a csh >which has a NEW option: "-b"! Its goal is to avoid just the thing described >above: Whereas the Bourne shell already has an option whose effect is to avoid the thing just described above - "-". Yup, just a dash by itself, as in #! /bin/sh - >the mnemonic for `b' is `break'; To quote the C shell source code: case 'b': /* -b Next arg is input file */ batch++; so I don't think the mnemonic was intended to be "break"....