Dale handles the Unix system security auditing for Lachman Associates Inc. He can be reached at 1901 Naper Blvd., Naperville IL 60540-1031.
Maintaining computer security, whether it be "open" systems like the Unix system or "closed" systems like those carrying government approved security ratings, involves a wide range of complex issues. Among the more pressing security issues are vulnerabilities in the user file system and threats to network security. Although, these areas are not necessarily related, similar approaches can be applied to each.
Computer system administrators are responsible for maintaining security, sanity, and day-to-day functionality. To some extent, users are capable of interfering with most or all of these goals and there are several possible security risks that can be caused by naive or inattentive users. Because threats to computer security can come from the outside in the form of hackers or computer intruders or from the inside in the form of malicious or disgruntled employees, there exists a need to protect users from each other and from themselves. With this in mind, I'll look first at the vulnerabilities that can occur in the user file system or the area where users store their programs and data, the "home directory."
File permissions determine whether a file is readable, writable, or executable and are an important component of information access control in any system. Consider the case where a malicious user wants to dupe a naive user into executing some command. Perhaps the simplest way to achieve this is by placing that command in an executable file owned by the naive user. This can be accomplished if the naive user has an executable file that is writable by users other than himself. The malicious user need only place the malicious commands in the writable executable file and wait for the user to run the contaminated program.
Consider the following "malicious command:"
cd$HOME rm-rf*
The first command, derived from Unix, changes the current directory to be the user's home directory. The second command recursively removes all files and directories located beneath the current directory. Thus, if a malicious user were able to cause an unsuspecting user to execute these commands, then the unsuspecting user would unknowingly remove all his files and directories. Of course, there are more subtle methods of attack available to the malicious user but this one is quite effective.
The vulnerability in this case stems from the fact that one user is "giving away" his execution privilege to another in the form of a writable executable file. The point is that all executable files imply an execution privilege -- some user will be invoking that process at some time or another. As such, all executable files should be write protected, just like the system executable files. Assuming that you have access to a list of a user's home directories, it is a good idea to look for vulnerable user files; a recursive algorithm like that in Example 1 shows a way of keeping tabs on vulnerable files.
FOR (every users' home directory) DO
checkdir (directory)
DONE
PROCEDURE checkdir (directory)
FOR (every file in the directory) DO
IF ((file is executable) AND (file is writable)) THEN flag this file
ELSE IF (file is a directory) THEN checkdir (file)
DONE
ENDPROCEDURE
Many people seem to believe that a write-protected file is always safe, even if it resides in a writable directory. A writable directory, however, allows other users to move (or rename) write-protected files. Once done, a file of the same name can be created in the same writable directory. As an example, consider the case where a user's start-up file, such as profile in Unix, is write protected, but the user's home directory is writable. A malicious user would be able to rename the start-up file and create a new file called "profile." This new start-up file could contain the malicious shell commands shown earlier. When the naive user logs in, the malicious start-up file will be executed by the log-in shell. Even a write-protected file is vulnerable if the directory it resides in is not write protected, too.
Files and directories should always be write protected, especially executable files and directories containing executable files. A file that is not executable but is writable might also present a problem. On systems where users are billed for disk utilization, for example, it would be cost effective for malicious users to append their data files to other users' writable files or place them in other users' directories. To address the more general issue of write protection, you might expand the program in Example 1 to that in Example 2.
FOR (every user's home directory) DO
checkdir(directory)
DONE
PROCEDURE checkdir (directory)
IF (directory is writable) THEN flag this directory
FOR (every file in the directory) DO
IF ((file is executable) AND (file is writable)) THEN flag this file
IF (file is writable) THEN maybe flag this file
ELSE IF (file is directory) THEN checkdir(file)
DONE
ENDPROCEDURE
The methods of attack described so far are often termed "Trojan horse" attacks. There are general search techniques that might be used to search for them. The code example in Example 3 seeks to eliminate the risk of a Trojan horse attack. You might also include in your examination of users' home directories a search for possible existing Trojan horses. To do so, you need to know what a Trojan horse might look like.
FOR (every file in the file system) DO IF (filename is in likely-list) THEN flag this file DONE
First, you know that to be useful, a Trojan horse program must be executed. Because users' commands are usually limited to the set of commands provided by the system, a successful Trojan horse might seek to masquerade as a system command. The simplest method of achieving this is to create an executable file with the same name as a commonly used system command. This type of Trojan horse can be placed in any writable directory anywhere in the file system.
An effective method of searching for existing Trojan horses consists of two steps. First, define a list of "likely" Trojan horse names derived from commonly used system commands. Then, search the entire file system for files having these names. The program might look like that in Example 3.
The actual implementation might be improved by including a mechanism for defining a list of files "authorized" to have a given name and ignoring those files in the search. By searching the entire file system, user and system areas are checked for the existence of potential security risks.
In the previous section, I described some tools that help to protect users from themselves and from other users. In this section, I'll focus more on outside threats to network security.
A network connection should be treated as a doorway to the world. The security considerations center on two basic issues: 1. What should be permitted to enter the system via the network? 2. What should be permitted to leave the system via the network?
One way to impose security on a networked system is by controlling the capabilities of the network software. This feature, if available, is usually provided through some sort of configuration mechanism. By imposing control over the network configuration you can impose some level of control over network security.
A brief description of some common network threats will help determine what type of network configuration is appropriate. Information entering your system may pose a variety of threats. If a remote user or site can arrange for an executable file to be placed on your system, then the threat of a Trojan horse or a virus attack exists. If a remote user or site can directly execute commands, then the threat of a direct attack on users or resources exists. Similarly, information leaving your system may divulge information useful to an intruder for planning other types of attacks. In general, your network configuration should be as limited as possible while still allowing a reasonable level of communication.
Having defined an acceptable network configuration, the next step is to automate the process of verifying the configuration. By way of example I'll describe the capabilities of a common network utility and show how such a verification program might be written. The example I'll use is derived from Unix where the most common network connection is called uucp (Unix to Unix copy).
The uucp facility is controlled by a series of configuration files that determine the capabilities of each remote system. The available capabilities include remote file copy in, local file copy out, and remote command execution. The configuration files determine what parts of the local life system can be accessed by a remote machine and what commands are available for remote execution. Thus, for the uucp facility the configuration files determine the network capabilities. The verification tool will examine the configuration files relative to the predefined level of network security desired.
Let's assume that some remote clients are not completely trusted and a reasonably controlled network environment is called for. In this case, you might elect to limit the access of all remote machines to one single directory. Thus, any information entering or leaving your system must be copied into or out of a single location. Similarly, you might elect to limit the remotely executable commands to those that are designed specifically for remote execution. In reality, this configuration is quite reasonable -- users can send and receive files, and commands designed for remote execution are made accessible.
The next step is to create a software tool that checks the network configuration files, and ensures that the network policy decisions are reflected in the actual configuration. Using the policy described earlier you can construct an example of such a program. Let's further assume that a directory named public has been reserved for the remote machine access point, and that the commands available for remote execution are called rmail and lpr. A simple checking program would look like that in Example 4.
FOR (each remote host that is defined) DO
IF (directories other than "public" accessible) THEN flag this host
FOR (each command name in command list) DO
IF ((command !="rmail") AND (command!="lpr")) THEN flag this command
DONE
DONE
For those of you familiar with uucp, this example may seem trivial. And in truth, a robust uucp checking program would perform many more checks than those shown here. Nevertheless, this program is effective and the explanatory text for uucp is neatly avoided. The reason the approach in Example 4 is effective is because it automates the task of maintaining security. Any time you can incorporate your security policy into an administrative tool, you have succeeded in achieving several goals. First of all, you acquire a means by which your administrator can audit the current state of the system against the criteria suggested by your security policy. Given such a tool, the administrative overhead of maintaining security is greatly reduced. Second, your security policy is no longer just a virtual set of guidelines. By incorporating the policy into your software tools, the policy itself has a physical representation. Given this, it becomes much more likely that the policy will be adhered to. By externalizing the security policy, the effort required to train a new administrator is also reduced.
Whether your concern is based on user vulnerability or attacks from remotely connected network sites the solution is the same. A good security policy is made much more effective, simpler, and easier to enforce when the tasks are automated. After all, policy without practice is no policy at all.