LINK="#3366FF" VLINK="#A000A0">



[ Table Of Contents ][ Answer Guy Current Index ] greetings   bios   1   2   3   4   5   6   7   8   9   10   12   13   14   15   16   17   18   19   20 [ Index of Past Answers ]

(?) The Answer Gang (!)


By Jim Dennis, Ben Okopnik, Dan Wilder, Breen, Chris, and the Gang, the Editors of Linux Gazette... and You!
Send questions (or interesting answers) to tag@lists.linuxgazette.net

There is no guarantee that your questions here will ever be answered. You can be published anonymously - just let us know!


(?) Operation Not Permitted on SUID Program

From Dann S. Washko

Answered By Karl-Heinz Herrmann, Jim Dennis

The permissions on a file like /usr/X11R6/bin/xterm are: rws--x--x which means the userid bit is set on execution so the process runs as root.

(!) [K.H.] That's a C program which needs no interpreter anymore
(!) [JimD]
You can write a very simple C wrapper program like:

*** WARNING, THIS SCRIPT HAS RISKY BUGS ***

#include <unistd.h>
int main (int argc, char ** argv) {

int err;
char *newenv[] = { NULL };

if ((err = execle("/usr/bin/pauseme", "pauseme", NULL, newenv)) < 0 ) {
	exit(err);
	}

return 0; // never reached!
}

*** risky *** risky *** risky *** risky *** risky ***

This uses one of the exec*() family of system calls, specifically the "varargs" forms with a pointer to a new environment. We don't use the system() or popen() library calls and we don't use any of the forms of exec*() that search the path nor those that retain the user's environment. These are all potentially exploitable bugs for SUID programs. So we have to use execve() or execle() to be reasonably secure. Additionally, I should have written a loop like:
{ int x; for (x = 0; x < 255; x++) close(x); }
To explicitly close all of my non-standard file descriptors (there are some exploits possible when exec()'ing programs with additional open files, becuase those persist through the system call).
Then I have to explicitly re-open the files or devices that I want my program to use.
These and, alot of other considerations depend on the exact program that is being invoked. For example, if your "pauseme" program might have vulnerable signal handling, than your wrapper program might have to do a setsid() and a fork() to detach signal propagation from the user's shell's process group.
Most importantly you'll have to do your own parsing and validation of any variable arguments and options that you want to allow the user of your script to set. If those involve filenames, you'll have to check those for access under the real UID (as opposed to the "effective" UID which is set by the ownership of the wrapper binary). File paths where any component of the patch is writable by a potentially hostile user are subject to race conditions if you attempt to check the ownership and permissions prior to opening it. (Generally you have to perform go through an lstat(), save the device/inode pair, then do your open() and use the fstat(), compare its device/inode the the one you stored, and then perform your permissions and ownership checks).
In other words, there are many "gotchyas" to writing C wrappers. Writing robust, non-exploitable C is difficult and there are whole books on the topic.

(?) Why is it then, if I create a script:


#!/bin/bash
pauseme

and give it the same permissions: rws--x--x

I get an error that the operation is not allowed if I try to execute the file as a non-superuser?

(!) [K.H.] When I ran into this I didn't get an error message -- the SUID bit was simply ignored, but anyway:
A shell script needs an interpreter. Since your /usr/bin/bash or whatever is most probably non SUID root it refuses (or simply can't change to SUID root) to run a script in SUID root. You as User are effectively running /usr/bin/bash on the script. So if /usr/bin/bash is not SUID it will run with your permissions and can't change to any other user.
It seems most *NIX OS'es share the opinion that shell scripts are that unsafe that they can't be allowed to run as SUID root, so simply setting SUID on the script won't be enough. It's even recommended that /bin/true (or /bin/false) be not shell scripts with "exit(0)" but instead little compiled C programs.
(!) [JimD] The SUID bit is ignored on shell scripts (and other #! text files) under Linux and on many other versions of UNIX. Historically this was due to an inherent race condition the way that the interpreter was executed and the shell script was subsequently opened. (I think newer versions of the Linux kernel have eliminated that race condition).
It is widely considered to be almost impossible to write a sufficiently robust shell script that it could be trusted to run SUID. Therefore the kernel's policy of ignoring the SUID/SGID bits persists.
(!) [K.H.] I don't think there is a simple walkaround for this with bash and friends.
(!) [JimD] Well, you could install sudo - I think it's by far the easiest of our solutiions here, and probably is the most robust and secure for your needs. It's included with most Linux distributions and available from any good package archive. You can read about sudo at its home page: http://www.courtesan.com/sudo
You'd install sudo, and configure it to run your command by using the 'visudo' command to edit the /etc/sudoers file. You could optionally specify the NOPASSWD option to allow a user or group of users to unconditionally access a command without needing to supply their password. sudo has been used by lots of sysadmins (it's more popular than any similar package like super or calife) for many years. There have been no critical bugs posted for it to my memory and only a couple of minor bugs (affecting uncommon configurations).
Here's a sample sudoers file:
# sudoers file.
# This file MUST be edited with the 'visudo' command as root.
# See the man page for details on how to write a sudoers file.
Defaults shell_noargs, set_home

%staff ALL=NOPASSWD:/bin/vi /etc/motd
In this example members of the "staff" group on "ALL" systems to which we distribute this sudoers file, can use vi to edit the /etc/motd (Message Of The Day) file, without using their password.
The thing that I always get into trouble with, when I'm making new sudoers entries is that part of about specifying which hosts this entry applies to. Normally I use use ALL=, since I tend to have quite limited sudoers lists, and therefore I have homogenous user/privilege matrices. NOTE: sudo isn't actually doing anything over the network, it isn't a remote access client/server or anything like that. This "host specification" feature of their sudoers file is purely to allow an admin to maintain one sudoers file and to distribute it (via some reasonably secure means, such as rsync over ssh) to all of their hosts.
If I were specifying hostnames or netgroups here, then the copy of sudo that I ran on mars.starshine.org would filter out all of the sudoer entries that didn't apply to mars.starshine.org and only consider giving me access to the commands that applied to my combination of user/group and host/netgroup.
The one disadvantage of using sudo is that your users have to remember to run "sudo ..." as part of their command. However, that's easy to work around by simply creating a wrapper shell script. This is a normal (non-SUID) shell script that simply does something like:
#!/bin/sh
exec /usr/bin/sudo /some/path/to/our/target/prog "$@"
(execute sudo, on the target program and pass our argument, preserving any quoting as we specified it).
(!) [K.H.] In perl it's handled by a special perl-version which has restricted possibilities and will run scripts SUID root (see also "perl -T" Tainted mode).
(!) [JimD] You can write a wrapper in Perl too. Perl is a scripting language, so it is subject to the same limitation as the shell. The kernel will not grant a perl process the effective UID of the owner of the SUID script file. However, the Perl interpreter checks the permissions itself, and can re-execute the open shell script using the optionally installed sperl (SUID Perl) wrapper/utility.
When sperl interprets a script, it performs many sanity checks. That makes it somewhat easier to write robust SUID perl scripts than SUID C wrappers. (Note, however, that sperl itself has had vulnerabilities; so questions of whether to install it on your system and which group(s) of users should be given access to the sperl binar(y|ies) do arise). There is a perlsec man page which focuses on the pitfalls and suggestions for writing robust Perl code.
You can read more than you want to know about secure programming for Linux and UNIX FAQs and HOWTOs at:
Shmoo's Security Links:
http://www.shmoo.com/securecode
Secure Programming for Linux and Unix HOWTO
http://www.linuxdoc.org/HOWTO/Secure-Programs-HOWTO/index.html
Avoiding Security Holes when Developing an Application:
http://www.linuxfocus.org/English/January2001/article182.meta.shtml
... and many others.
(!) [K.H.] If anyone knows one or has details on where the high security risc comes in with scripts (lets assume one which doesn't use any commandline arguments) I'm alos quite interested.


This page edited and maintained by the Editors of Linux Gazette Copyright © 2001
Published in issue 67 of Linux Gazette June 2001
HTML script maintained by Heather Stern of Starshine Technical Services, http://www.starshine.org/


[ Table Of Contents ][ Answer Guy Current Index ] greetings   bios   1   2   3   4   5   6   7   8   9   10   12   13   14   15   16   17   18   19   20 [ Index of Past Answers ]