Creating Vulnerabilities: An Example



Now, let’s walk through a scenario using all of the previously mentioned items to understand how bad things happen to well-intentioned programs. The e-mail program mentioned at the beginning of this section is a great example. Later, we will walk through all the steps of the SDLC and discover what can go wrong.
  1. Concept and proposal: XYZ Company decides that the world would be a better place if they could send e-mails to each other using a special program that presents and manages the e-mails better than any other program on the market. It will use a specially adapted format that puts unique functionality inside the messages.What could go wrong? Complexity has been introduced into a system that is relatively simple. Similar to the GDI+ API example, more complexity creates more opportunity for faults. If the new functionality is so critical to the success of a product, the security of the unique parts of the system will have to be stressed from the outset of the project. At a minimum, the reliability and security of the key components should almost be a feature in the concept.
  2. Requirements gathering: The detailed requirements for all the functionality are gathered from those who have created the concept. There is tremendous enthusiasm for all the great capabilities that will be included and how they will enhance the business model. This should lead to great things one day. What could go wrong?None of the requirements include security or reliability features. These capabilities should be part of any application requirements. Every idea that comes out of the requirements gathering process creates a certain amount of scope creep that can lead to fundamental design flaws. In addition to minimizing growth in cost and schedule, containing scope during the requirements phase can minimize the opportunity for the creation of vulnerabilities.
  3. Design: Details of the application, including databases, file systems, and operating system compatibility, are designed on paper. What could go wrong? The design, like the requirements, considers only functionality. All of the focus is on getting the application working. Some work should be put into having the application create restrictive areas of data flow prior to any processing that can validate inputs and outputs to assure that they stay within the application’s functional parameters. Points of data validation and authorization should be established for interprocess communications. One process should be able to verify that the data coming from another process is valid and that the source process is legitimate and operating with authorization. Some threat modeling should begin at this phase. By identifying the areas that are at high risk for exploitation, the team can focus efforts on making sure the most vulnerable components are especially hardened.
  4. Code: After the design is complete, the programs are written. What could go wrong? This is the most famous part of systems development in the vulnerability world. The programmers fail to agree on best practices for coding to avoid problems like buffer overflows or other such vulnerabilities. Secure coding best practices should be used, such as bounds checking in dynamically sized data structures and input validation.
  5. Test: Testing goes at a feverish pace. Bugs are found and quickly fixed. Testers keep verifying that all of those requirements that did not include security components are correctly implemented. What could go wrong? Testing of the application has the most time pressure. Managers want to see the product in production and have little tolerance for delay. Testers focus only on functionality and often skip regression tests. This is when developers break things for the sake of getting through this phase. Some kind of penetration testing of the product should be conducted with guidance from the previously mentioned threat model.
  6. Deployment: Finally, the product is ready. It is packaged and shipped to unsuspecting customers worldwide. Documentation is included that explains how to install and use the program. What could go wrong? The documentation does not include any information about the most appropriate, secure configuration of the system upon which the software will run. It does not say that certain ports should not be open on the network interface to any IP address except the supporting server. Some plans should be made by the development team to respond to vulnerabilities that are discovered. At this point, a much larger group of penetration testers will begin working on the application.
  7. Identification: An interested user of the application wonders what would happen if an e-mail is sent with an unusually designed message that matches the processing requirements for a particular module of the e-mail software. But, the user structures the message to be larger than that module’s standard data structure calls for. The program crashes, leaving the operating system with fragments of the message in memory.
  8. Exploitation: The user now wonders whether he could imbed a program in the previously mentioned message to stay in memory and run. It works. A small “hello world” program runs.
  9. Compromise: The now-very-excited user develops a more improved version of the message that creates more messages using the program and sends copies of itself to others in the address book, an address book that is made more accessible by the powerful functionality built into the e-mail software. The new product’s first e-mail worm is born!
(Guidance: Remove the vulnerabilities that could be exploited early and often. Long term, this decreases the workload on IT and Security, and lowers the cost of detection and prevention tools to achieve a similar level of security.)

Vulnerability Creation Process



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:
  1. Concept and proposal: A business or government operation has a required function.
  2. Requirements gathering: The developer analyzes the requirements and clarifies them with the business.
  3. Design: A design is developed against the requirements.
  4. Code: Programmers write the code and test it.
  5. Test: If we are a thorough development operation, the quality assurance team makes sure that the code meets the requirements.
  6. 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:
  1. 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.
  2. Exploitation: A person then tries to see how many ways this flaw can be manipulated to exceed the original requirements of the software.
  3. 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.

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.

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.

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.

Popular Posts