Cover V10, I09



New Messages

Readers: Thank you for your excellent feedback during this past month. I received many letters in particular about the "Fastest OS for Network Applications" article featured on the July Web site. The letters are too numerous to reprint here, but many contained similar points. Specifically, the FreeBSD users among you expressed concern over the lack of kernel tuning done for the test. You made your case so strongly that the authors agreed to retune and retest. Their fresh results, and the readers' recommendations for proper tuning appear on the Sys Admin Web site.

I also received a most useful letter providing additional tips and outlining inaccuracies in the "Dangers of SUID Shell Scripts" article (June issue). It appears below.

Thanks again for your support of the magazine. Write to me with your comments at:

Subject: Dangers of SUID Shell Scripts

I read the article "Danger of SUID Shell Scripts" in the June issue of Sys Admin. While there are many pitfalls in writing SUID shell scripts, some of which you pointed out, the article contains several inaccuracies and makes it seems as if it is impossible to write SUID shell scripts that are secure. Writing SUID scripts in any language requires a great deal of understanding and should not be done without a complete understanding of the issues. As far as csh is concerned, I agree with you. It is not an appropriate shell for writing any script, let alone, SUID scripts.

First, let me describe the inaccuracies. You state that if you invoke the script as

change-pass "user;cp /bin/sh /tmp/sh;chown root /tmp/sh;chmod 4755 /tmp/sh"
that the shell will run the cp command and create a SUID shell program. No shell that I am aware of would do this. Shell syntax is checked before parameter expansion, and the results of expansions are not automatically re-evaluated. Perhaps, you are thinking of the effects of eval. Your warning about the effects of special characters would make sense if you were describing to C programmers the danger of calling system() from a SUID program. The system("string") call invoked sh -c "string" and special characters in string would be processed by the shell grammar.

A second error relates to the warnings about IFS. ksh, and POSIX-conforming shells, will not split /usr/bin/passwd into usr bin passwd like older Bourne Shells did. Only characters generated as the result of substitutions are split with IFS. Moreover, ksh ignores IFS settings from the environment and resets IFS to the default value at the start of each script. Better advice would be to set IFS='' if you do not require word splitting, and to put set -o noglob if you do not require file name generation. Your current example does not put double quotes around $user, so that a user could run from a directory that contains only a file named root, and /usr/bin/passwd would be invoked with the argument root by passing * as the name.

The dangers of /tmp files have also been eliminated with recent versions of ksh. The /tmp file names are not guessable, and they are created with an exclusive mode that prevents their use in any way.

The timing problem you mention in the article depends on the system that you are running on. On some systems, when the system encounters #!, it opens this file and then passes down /dev/fd/n, where n is the open file descriptor number, rather than the name of the script. The shell than reads the script from this file descriptor. On these systems there is no race condition. Moreover, the script does not need to be readable in order to run. On systems that don't have this feature, if the SUID script has been invoked by ksh, the environment variable "_" will contain the pathname of the invoking script when the script is invoked. The script could check this against the expected name to avoid allowing a symlinked version to be invoked so that this race condition could not be exploited.

Finally, you left out additional advice on writing secure SUID shell scripts. For example, if a user creates a symbolic link named -i to the SUID script, and you use #! /bin/ksh in line 1, then ksh will be invoked as ksh -i, which would specify an interactive shell. (Actually, ksh is paranoid about running interactive SUID shells, so it will fail). Adding a -, or -- after #! /bin/ksh will prevent this. Additionally, you do not warn about the dangers of using eval, or the danger of not quoting parameter and command substitution.

I have included a list of advice that I would give to users that wish to create SUID shell scripts.

  • Put #! /bin/ksh - as the first line, rather than just #! /bin/ksh.
  • Manually set your PATH using only absolute names.
  • If you do not require file name expansion, use >set -o noglob.
  • If you do not need word splitting, use IFS=''. Otherwise, use double quotes around every expansion. You might still need to use double quotes to handle the case in which the expansion yields the null string.
  • Invoke unset on each variable before you use it.
  • Check each argument to see whether it meets the required specifications.
  • Do not use eval in your script.
  • Avoid creating file unless absolutely necessary. Be careful about any files that you create. Use set -o noclobber before creating files so that they will be created with exclusive use.
  • Before you create any file, add a trap on EXIT to remove that file so that if your script aborts or exits, the file will be removed.

David Korn
Author of ksh