Exploit-based Bandwidth/Network Analysis Framework

Master’s Project

Matthew Weaver

University of Colorado at Colorado Springs

March 2007

“I can’t be as confident about computer science as I can about biology. Biology easily has 500 years of exciting problems to work on. “– Donald Knuth

Abstract

Background: Traceroute and Ping are the two most common algorithms/implementations for basic bandwidth analysis and network troubleshooting. However, these only work in networks where every node strictly adheres to the standards for network protocols maintained by W3C (World Wide Web Consortium). An exploit, on the other hand, could be used to infect machines on a network and provide metrics while overcoming the problems inherent in the traditional approach.

Results: After examining real world exploits and devising a similar Windows service, and exploiting it with a buffer overflow exception we can infect said service and perform our testing.

Conclusion: Limited success on Windows platforms indicates that this is a feasible hypothesis: one can measure bandwidth using an exploit. However, real world conditions will prevent this from ever being used outside of an academic environment.

Table of Contents

Exploit-based Bandwidth/Network Analysis Framework......

Abstract......

Preface......

Disclaimer......

Introduction......

Exploits......

Worms......

The Buffer Overflow Exploit......

Network Protocols......

Sockets......

Winsock......

COM in Windows......

Doing some Recon......

Hypothesis......

Related Work......

A Framework for Bandwidth Testing......

Implementation......

Conclusion......

Future Work......

References......

Demonstration

Preface

Writing shell code for Windows is a very dangerous proposition, I crashed a lot of machines before I simplified my approach. You have to hand it to those who are willing to spend hours perusing and testing Windows services, looking for flaws. Not to mention writing and testing shell code and exploits.

I certainly am not done with this theory, I am not sure if I would continue to pursue it in an academic setting, but I am still convinced I can improve on it. That kind of thinking is what makes this all take so long.

As a result, this project has resulted in the sniFF framework, which can be used standalone, however due to the ever-changing nature of exploits it is recommended that further research be done with the Metasploit framework[1].

All that said, I appreciate the help and cooperation of all of my friends, family, coworkers, and especially Dr Chow. At least reading this paper should help you sleep.

Matt Weaver

Spring 2007

Disclaimer

This paper discusses the creation of worms, the generation of shell code, and Windows x86 assembly. All discussion and included code are intended for educational purposes only. The author is not responsible for any damage done to networks or network systems.

As an aside, I urge caution with using the included exploit. It proved to be extremely unstable in testing.

Introduction

Computer viruses date back to the early days of UNIX. In the late seventies and early eighties, network administrators at MIT used worms to communicate and accomplish tasks on the network. Some of these early worms did fairly mundane tasks: backing up systems, sending messages, etc. In the late eighties, with the rise of networks (including the various incarnations of DARPAnet) vulnerabilities were bound to arise.

Modern computer systems run many network based services. A typical install of Windows XP runs services to check for updates with Microsoft servers around the world, network services to keep the IP address up to date, inter operating system messaging for asynchronous processing, and a host of other potential “holes”. Each update to these systems or additional products installed creates more potential vulnerabilities (Kienzle 8).

Networks themselves, are juxtaposed collections of various machines, routers, devices, firewalls, and other miscellany hardware that make the diagnosis of problems very difficult. Most of this is due to the ever evolving area of network security: smarter routers, more firewalls, Intrusion Detection Systems (IDSs), subnets, and tunneling. All of this can make network troubleshooting a black art. Even the simplest problem, say out of date DNS data can be difficult to pinpoint: more so on large corporate networks where resources may span thousands of miles. Running a simple Traceroute between two IP addresses can be an infuriating task.

To that end, most of these problems are the result of trying to mitigate security problems. However, despite these precautions, computer networks are infected with viruses every day. New ones are written as soon as old ones are discovered. This results in a slippery slope of constant patches to software and operating systems.

Viruses break into systems by exploiting weaknesses in running software: invariably through various network visible services. Since viruses still infect networks, is it possible to modify an exploit or author a new one that can be used to measure bandwidth to machines on a network?

This proves to be a very dangerous proposition, as much as it is a novel idea: Windows viruses are very unstable and the more complex they are the more likely they are to fail, causing unstable behavior crashing or permanently crippling a Windows install.

Exploits

Due to the complexity of modern software and the hierarchy of software running on modern computers, glitches and bugs are inevitable. A given program can only do what it is written to do, however this may not be what is intended:

A man is walking through the woods, and he finds a magic lamp on the ground. Instinctively, he picks the lamp up and rubs the side of it with his sleeve, and out pops a genie. The genie thanks, the man for freeing him and offers to grant him three wishes. The man is ecstatic and knows exactly what he wants.

‘First,‘ says the man, ‘I want a billion dollars.’

The genie snaps his fingers, and a briefcase full of money materializes out of thin air.

The man is wide-eyed in amazement and continues, ‘Next, I want a Ferrari.’

The genie snaps his fingers and a Ferrari appears from a puff of smoke.

The man continues, ‘Finally, I want to be irresistible to women.’

The genie snaps his fingers and the man turns into a box of chocolates.” (Erickson, 12)

In the same way, software regularly has to interact across networks and run services that expose interfaces that may allow behavior unintended by the developers. But they operate as they are coded to.

These glitches are what exploits expose.

Worms

The term ‘worm’ originated from author John Brunner, in his novel Shockwave Rider (first published in 1925). Two researchers from Xerox PARC, John F Shock and John A Hupp, used the term in their paper The Worm Programs (Comm ACM, 25(3):172-180, 1982).

These kinds of programs represent one of the most interesting and challenging forms of what was once called distributed computing. Unfortunately, that particular phrase has already been co-opted by those who market fairly ordinary terminal systems; thus, we prefer to characterize these as programs which span machine boundaries or distributed computations.” - Hupp and Shock, 172

Shock and Hupp took a very informal “stab” at creating a worm.

Our work did not start out specifically addressing formal conceptual models, verifiable control algorithms, or language features for distributed computation, but our experience provides some interesting insights on these questions and helps to focus attention on some fruitful areas for further research.” - Hupp and Shock, 173

Their approach is not fundamentally different than the subsequent twenty seven years. But the reason that academic research in the area of “worms” continues this ad hoc approach has less to do with the concepts behind a “worm” than the changing nature of computing security.

The worms first utilized by researchers in the early 80s were driven by a combination of curiosity and usefulness. For example, researchers at MIT used a ‘Town Crier’ worm to send messages to all users on the network.

Early worms did not face the barriers to access that a modern worm designer faces. Simply Berkley-standard C programs could open a socket on a machine without having to fight through firewalls.

The first worm of import, in the ever evolving realm of security was the infamous “Morris Worm” written by Robert Tappan Morris (launched on November 2, 1988), then a student at CornellUniversity. Through simple dictionary password hacks, Morris worm was intended to “map” the known internet, sending vital information back to the root of the infection (if we imaging the propagation path as a tree).

The Morris Worm, however, was riddled with design flaws. As a result machines would be reinvested ad nauseum until the machines would crash. At this point in history, real security was never a concern: the Internet was used largely by academic institutions and government agencies. Widespread public access was not a concern. As a result, the Morris Worm took advantage of flaws in Unix mail services and poor password choices for privileged accounts (who knew people would use the same root passwords?)

The Buffer Overflow Exploit

Many network exposed services are written in languages that do not perform bounds checking on requests. When a request or call passes more data in than expected, that data overwrites the memory in the stack, effectively putting that “extra data” where it can be executed.

Here is an example of “unsafe” code in C:

Because “strcpy” does no bounds checking, this program promptly crashes at run time. As you can see, the string passed in is far too large to fit into 5 characters (4, in C strings have to end in ‘\0’ or null).

What makes this so dangerous is that most network level programming is done in C, since C has limited string manipulation “strcpy” is used frequently.

So what can be done with this kind of code? At runtime, the stack is filled with linked assembly, as binary that will be executed. To take advantage of this, we need to copy our own binary so that the excess will contain more instructions.

Since this project focused on exploiting a Windows vulnerability, we will write a very simple program to push onto the stack. After the program is compiled and linked, we want to disassemble it and find the assembly instructions. In a Windows environment there are no installed tools to handle this. As a result, you most likely will need to install Cygwin with GCC and the GDB disassembler. After code has been compiled, it can be disassembled in GDB and GDB will also give us the binary that represents that code. This binary is called, “shell code”.

First, we write a simple program… ‘Hello World:

This generates a block of binary that looks like this in C:

The binary instructions are not quite right. What if another program is trying to execute in the space we want to occupy? If this happens at run time a segmentation fault will be thrown by the operating system. Not a good thing.

In his paper “Smashing the Stack”, Aleph One points this out. Of course, it’s still not that simple. More work has to be done to get the correct binary to run our code. This is a tedious process… even small programs take a bit of memory magic to process.

It gets worse on an x86 Windows box.

In Linux distros, to perform most tasks, all one has to do is make the appropriate system call. In Windows, a bit more work is involved:

The problem is, though, that system call numbers are prone to change between versions of Windows whereas Linux system call numbers are set in stone. This difference is the source of the problem with writing reliable shell code for Windows and for this reason it is generally considered ‘bad practice’ to write code for Windows that uses system calls directly vice going through the native user-mode abstraction layer supplied by ntdll.dll.” (Skape, 6)

To do anything useful in Windows, one first has to locate kernel32.dll. Fortunately through this mechanism, you can load any system libraries you may know about. Of course, if you can’t do that other problems may arise. Ah, but even the address of kernel32.dll vary based upon the version of Windows, other software that may have changed that location, etc. Once again, it’s a little more difficult than you suspect to do exactly what you want.

But it is possible. The sheer number of buffer overflow exploits that have wreaked havoc on Windows systems shows that it is done. However, most of these are rather unstable. Why? Some of it is all of the aforementioned. The other part… the worse part, is that Windows shell code is much more difficult to write than Linux shell code. It’s a process that is very easy to mess up.

Network Protocols

Communication between two machines or devices is only possible if they can both speak the same language and if they both can talk to each other. Some of this is hardware, some of this falls to something above the physical layer. Modern systems communicate using the protocol system(s) defined by the Open Systems Interconnection (OSI) seven layer model:

Figure 1: Seven layer model ()

At the lowest end of the model is the physical connection required to transmit the data. Towards the middle, we cross over into a gray area that is controlled by either hardware (firmware) or software running on the host system, until we are purely at the data layer. Connecting to a machine to infect it is more than just sending data to another machine.

To infect a machine, it is not enough to connect to it, nor is it enough to pack the payload of the data with the exploit. What are we typically seeking to do? The most effective and useful exploits look for the classic security flaw: the buffer overflow exception.

Sockets

In modern OS network services run base level code to handle the lower levels of communications protocols. This is done through socket programming, and usually with C based on the Berkley socket programming model. There are some pitfalls to this, which enables us to exploit systems, but we will address this later.

There are many types of sockets which deal with a variety of protocols in the seven layer model. For this research there are really only two of concern: stream sockets and datagram sockets. A stream socket is used by most network services for a variety of reasons: transmission order and protocol being two of the most important. By order we mean that if one service opens a socket with a machine and is sends two packets containing payload data “Fred” and “Barney” they will arrive in that order. Not only that but streaming sockets can be secure. Web browsers use stream sockets to transfer text (HTTP in the presentation layer). FTP uses streaming sockets as well. Why? Stream sockets run TCP (RFC-793[2]). TCP requires a three way hand shake: SYN, ACK, SYN-ACK. First a machine must verify that the target machine is there and that it is ready for a connection. Basically, like a normal, human conversation.

Datagram packets, on the other hand, utilize UDP (RFC-768[3]). UDP is unique in that there is no hand shake involved. UDP data is sent whether or not the target exists or responds. As a result UDP is fast, much faster than data transmitted over TCP. UDP can carry much of the same payload data, but because no handshaking is involved, you can’t guarantee that the target received it, or which order it was received in. UDP is still used, specifically for DNS queries (whether or not this is prudent).

Winsock

As Windows has evolved, the fundamental specification for network programming has resulted in the development of Winsock, short for Windows Sockets. Winsock is a specification first developed in 1992. Originally, Windows provided little to no networking support, most of it based on NetBIOS (from IBM). Microsoft had attempted to define their own network protocol, ignoring the TCP/IP stack.

Winsock consists of two interfaces, an API[4] for programmers seeking to write network based applications and an SPI (Service Provider Interface) that allows developers to also add modules to support new protocols.

At first, Microsoft just followed the Berkley convention: the methods and methodology was identical to the common Berkley UNIX implementation that most other vendors had adopted. However, Microsoft soon chose to add custom functionality and provide wrappers over the standard Berkley code, in addition to a list of custom exceptions.

The additional capabilities built into the current Winsock API (Winsock 2) are critical for this project, to allow us to safely measure bandwidth, as we will explore in the implementation.

Internet Connection Message Protocol

Modern measurement tools depend on the Internet Connection Message Protocol (ICMP) as described in RFC 792[5]. ICMP is designed to provide meaningful communication between software layers and hardware. Using ICMP diagnostic measurements can be generated including, destination unreachable, source quench, redirect, timestamp, timestamp reply, information request, information reply, and the last two of most important significance, echo and time exceeded. These messages general have a simple format.

Echo consists of a UDP datagram that looks like such:

0 1 2 3

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Type | Code | Checksum |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Identifier | Sequence Number |

+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

| Data ...

+-+-+-+-+-

Where type can be either 8 for an echo message or 0 for an echo reply message. Everything else is typical of such data. But, ICMP messages do not look alike. Time exceeded messages return a header that looks more like this: