Uninformed: Informative Information for the Uninformed

Vol 2» 2005.Sept


Introduction

A common impediment to the implementation of portable and reliable exploits is the location of a return address. It is often required that a specific instruction, such as a jmp esp, be located at a predictable location in memory so that control flow can be redirected into an attacker controlled buffer2.1. Many times, though, the locations of the instructions will vary between individual versions of an operating system, thus limiting an exploit to a set of version-specific targets that may or may not be directly determinable at attack time. In order to make an exploit independent of, or at least less dependent on, a target's operating system version, a shift in focus becomes necessary.

Through the blur of rhyme and reason an attacker might focus and realize that not all viable return addresses will exist indeterminably in a target process' address space. In fact, viable return addresses can be found in a transient state throughout the course of a program's execution. For instance, a pointer might be stored at a location in memory that happens to contain a viable two byte instruction somewhere within the bytes that compose the pointer's address. Alternatively, an integer value somewhere in memory could be initialized to a value that is equivalent to a viable instruction. In both cases, though, the contents and locations of the values will almost certainly be volatile and unpredictable, thus making them unsuitable for use as return addresses.

Fortunately, however, there does exist at least one condition that can lend itself well to portable exploitation that is bounded not by the operating system version the target is running on, but instead by a defined window of time. In a condition such as this, a timer of some sort must exist at a predictable location in memory that is known to be updated at a constant time interval, such as every second. The location in memory that the timer resides at is known as a temporal address. On top of this, it is also important for the attacker determine the scale of measurement the timer is operating on, such as whether or not it's measured in epoch time (from 1970 or 1601) or if it's simply acting as a counter. With these three elements identified, an attacker can attempt to predict the periods of time where a useful instruction can be found in the bytes that compose the future state of any timer in memory.

To help illustrate this, suppose an attacker is attempting to find a reliable location of a jmp edi instruction. The attacker knows that the program being exploited has a timer that holds the number of seconds since Jan. 1, 1970 at a predictable location in memory. By doing some analysis, the attacker could determine that on Wednesday July 27th, 2005 at 3:39:12PM CDT, a jmp edi could be found within any four byte timer that stores the number of seconds since 1970. The window of opportunity, however, would only last for 4 minutes and 16 seconds assuming the timer is updated every second.

By accounting for timing as a factor in the selection of return addresses, an attacker can be afforded options beyond those normally seen when the address space of a process is viewed as unchanging over time. In that light, this document is broken into three portions. First, the steps needed to find, analyze, and make use of temporal addresses will be explained. Second, upcoming viable opcode windows will be shown and explained along with methods that can be used to determine target time information prior to exploitation. Finally, examples of commonly occurring temporal addresses on Windows NT+ will be described and analyzed to provide real world examples of the subject of this document.

Before starting, though, it is important to understand some of the terminology that will be used, or perhaps abused, in the interest of conveying the concepts. The term temporal address is used to describe a location in memory that contains a timer of some sort. The term opcode is used interchangeably with the term instruction to convey the set of viable bytes that could partially compose a given temporal state. The term update period is used to describe the amount of time that it takes for the contents of a temporal address to change. Finally, the term scale is used to describe the unit of measure for a given temporal address.