20.3. Vulnerability Classification
Vulnerability classification frameworks describe security flaws from various perspectives. Some frameworks describe vulnerabilities by classifying the techniques used to exploit them. Others characterize vulnerabilities in terms of the software and hardware components and interfaces that make up the vulnerability. Still others classify vulnerabilities by their nature, in hopes of discovering techniques for finding previously unknown vulnerabilities.
The goal of vulnerability analysis is to develop methodologies that provide the following abilities.
The ability to specify, design, and implement a computer system without vulnerabilities. The ability to analyze a computer system to detect vulnerabilities (which feeds into the Flaw Hypothesis Methodology step of penetration testing). The ability to address any vulnerabilities introduced during the operation of the computer system (possibly leading to a redesign or reimplementation of the flawed components). The ability to detect attempted exploitatons of vulnerabilities.
Ideally, one can generalize information about security flaws. From these generalizations, one then looks for underlying principles that lead toward the desired goals. Because the abstraction's purpose is tied to the classifiers' understanding of the goal, and of how best to reach that goal, both of these factors influence the classification system developed. Hence, the vulnerability frameworks covering design often differ from those covering the detection of exploitation of vulnerabilities. Before we present several different frameworks, however, a discussion of two security flaws will provide a basis for understanding several of the problems of these frameworks.
20.3.1. Two Security Flaws
This section presents two widely known security vulnerabilities in some versions of the UNIX operating system. We will use these vulnerabilities as examples when comparing and contrasting the various frameworks.
The program xterm is a program that emulates a terminal under the X11 window system. For reasons not relevant to this discussion, it must run as the omnipotent user root on UNIX systems. It enables the user to log all input and output to a log file. If the file does not exist, xterm creates it and assigns ownership to the user; if the file already exists, xterm checks that the user can write to it before opening the file. Because any root process can write to any file in the system, the extra check is necessary to prevent a user from directing xterm to append log output to (say) the system password file and gaining privileges by altering that file.
Suppose the user wishes to log to an existing file. The following code fragment opens the file for writing.
if (access("/usr/tom/X", W_OK) == 0){
if ((fd = open("/usr/tom/X", O_WRONLY|O_APPEND) )< 0){
/* handle error: cannot open file */
}
}
The semantics of the UNIX operating system cause the name of the file to be loosely bound to the data object it represents, and the binding is asserted each time the name is used. If the data object corresponding to /usr/tom/X changes after the access but before the open, the open will not open the file checked by access. So if, during that interval, an attacker deletes the file and links a system file (such as the password file) to the name of the deleted file, xterm appends logging output to the password file. At this point, the user can create a root account without a password and gain root privileges. Figure 20-6 shows this graphically.

The Internet worm of 1988 [292, 386, 757, 858] publicized our second flaw. It continues to recurfor example, in implementations of various network servers [200, 201, 202]. The finger protocol [964] obtains information about the users of a remote system. The client program, called finger, contacts a server, called fingerd, on the remote system and sends a name of at most 512 characters. The server reads the name and returns the relevant information, but the server does not check the length of the name that finger sends. The storage space for the name is allocated on the stack, directly above the return address for the I/O routine. The attacker writes a small program (in machine code) to obtain a command interpreter and pads it to 512 bytes. She then sets the next 24 bytes to return to the input buffer instead of to the rightful caller (the main routine, in this case). The entire 536-byte buffer is sent to the daemon. The first 512 bytes go into the input storage array, and the excess 24 bytes overwrite the stack locations in which the caller's return address and status word are stored. The input routine returns to the code to spawn the command interpreter. The attacker now has access to the system. Figure 20-7 shows the changes in the user stack.

 |