For the uninitiated, the idea of vulnerability management in all its details is a lot to grasp. But if you remember Computer Science 101, there is input, output, and processing. Every program has this. An e-mail program accepts inputs, messages sent by someone. Then, the program processes it, breaking the message into parts that can be understood by a person. Finally, the program has outputs: it displays the e-mail message to the user, with all the related formatting.
This all seems quite simple but we should consider how these programs are developed now:
- Concept and proposal: A business or government operation has a required function.
- Requirements gathering: The developer analyzes the requirements and clarifies them with the business.
- Design: A design is developed against the requirements.
- Code: Programmers write the code and test it.
- Test: If we are a thorough development operation, the quality assurance team makes sure that the code meets the requirements.
- Deployment: The business installs the software on production systems and announces it to the world. Or, the software is shrink-wrapped and shipped to stores everywhere.
If this sounds familiar, it should be; it’s the systems development life cycle (SDLC). Each step along the way, there are problems that result in missing something that users ultimately regret. So, let’s complete the story:
- Identification: A hacker, researcher, or end user finds a flaw in the processing of inputs, outputs, or interaction with other system functions. This could also be considered hidden or new functionality.
- Exploitation: A person then tries to see how many ways this flaw can be manipulated to exceed the original requirements of the software.
- Compromise: An additional piece of software is developed and deployed to recreate these circumstances and automate the new functionality.
This, too, should sound familiar. It is the world we have lived in for many years. Three factors have made items 7 through 9 more commonplace and more problematic than ever: complexity, communication, and interoperability.
1 Complexity
The environment and systems upon which these programs operate are enormously complex. Layers upon layers of software components underlie any given application. Even the most fundamental computer software component is absurdly complex, with myriad modules to perform critical functions interacting with more complex and diverse hardware options. Consider, for example, the traditional “hello world” program that all computer science students learn when starting out. The task is simply to write a program that will output a text string. In a graphical environment, this type of program can become quite large. First, there is the interpreter that must run to load the program, read the instructions, and perform the tasks. Additionally, the program must interface with the environment through a library. This is a piece of code that takes the simple programming instructions and applies them to the operating environment. In this case, the library may be a general input/output library to create a window on the screen and map the text into it. To support this library, the operating system has several additional functions that must be loaded to communicate with the drivers supporting the graphics card and display. This creates a very complex chain of connections that are all potential attack vectors in the security world.
To demonstrate how this complexity can quickly be exploited through another component in the system, let’s create a hypothetical program. The job of this program is simple. It is supposed to accept input from a file and display it on the screen. When the user receives a file in e-mail, he saves the attachment to the local file system and runs the program we have created. Selecting the file as input, the program does exactly what it is supposed to do. This is where things go wrong and the finger-pointing begins.
The file contains a sequence of non-displayable bytes that cannot be processed by the general I/O library used by the program. It causes that library to jump to a memory location whose address is a remnant of the file. The remaining contents of that file are left in the memory space where the library was running. That part of the file is a malicious program that gains control of the computer but acts as though the library completed its task successfully.
In this example, the program that we wrote did what it was supposed to do. But the complexity of the underlying components was not accounted for in our code. The argument begins when you want to decide who should fix this problem. Should you have validated the input data for proper display before processing? Should the library author have done so? This argument, though interesting, is beyond the scope of this chapter. In some cases, however, you will have to think about this when evaluating in-house and vendor-supplied software.
2 Communication
IP is a fundamental protocol that enables our programs to operate over distances. It allows more complex software to work in modules processing information at various levels in a hierarchy. Those levels are often separated by network connections that are filled with IP-based messages.
But IP was originally designed for the Department of Defense to provide resilient communications in the event of a nuclear attack; the idea being that if one location in the network were destroyed, traffic could be routed around it to continue operation. This functionality was built with the idea that the military would have a closed communications system. Unfortunately for the military and the world, IP has come to be used in open networks where just about anyone can gain access. This is a clear case of the misapplication of a technology. It is being used far beyond its original use case.
The ability of one system to communicate with another and subsequently to manipulate the inputs and internal processes of a peer on a network removes the physical barriers to abuse. Remote access is a key element of enhancing productivity. But like everything we do to improve the work environment, this is a double-edged sword. The problem is further compounded by the fact that networks are complex and tools are put in place to make them simpler.
The DNS is a good example of this. At one point, it became too difficult to remember all those thousands and then millions of IP addresses for applications whose components are spread throughout the world. So, someone invented DNS to allow us the convenience of easy-to-remember names. This system was never designed to be secure, only efficient. It has since been widely abused by hackers. Even the underlying software of DNS servers is under constant attack. This trend is likely to continue unabated.
3 Interoperability
All manufacturers of hardware and software components today strive for this golden capability. If they don’t, they have a smaller market. Imagine an operating system that does not use the common SMB file sharing protocol, or a printer that only works with a particular word processor. This would be unacceptable. This problem was recognized early in the computer industry as technologies gained some level of ubiquity in corporate and government operations. Consider, for example, EBCDIC and ASCII. Both of these data encoding schemes were created for the purpose of making the exchange of information among systems and programs consistent, and the processing of such information repeatable with minimal variation.
I am not suggesting that these encoding methods are in any way inherently vulnerable. But it is the compatibility of all the components of a system that make it more vulnerable. A particular program that processes ASCII characters may have a flaw. If that same ASCII processing is used in several programs, then the impact of the vulnerability is all the worse. One part is used to exploit another part. A buffer overflow in one program can give someone access to the entire system. If a standard component used broadly in a computer system by a variety of programs turns out to have a vulnerability, then it is possible that all programs that use the component could be vulnerable.
A more concrete example of how this interoperability has led us to serious, widespread vulnerabilities is the Microsoft Windows GDI+ buffer overflow.[*] The GDI+ API provides programmers with a set of libraries for displaying and printing information. In this case, the component that processes JPEG images fails to parse the inputs in a safe manner. When the JPEG image is formatted poorly, the program fails and allows that remaining portion of the image to stay in computer memory and possibly be executed as a program. This is obviously a problem.
But the problem is compounded by the fact that the GDI+ product is an API. This means that many programmers all over the world can use a version of the API to perform functions. The installers for these programs, in some cases, want to be sure they have the right version of the GDI+ API available. So, they install their own copy. Thus, some computer systems could end up with several vulnerable versions of the API in different parts of the target’s file system. To address this, every vendor would have to identify and patch their products.
0 comments:
Post a Comment