Uninformed: Informative Information for the Uninformed

Vol 7» 2007.May


Stack-based buffer overflows are generally regarded as one of the most common and easiest to exploit classes of software vulnerabilities. This prevalence has lead to the implementation of many security solutions that attempt to prevent the exploitation of these vulnerabilities. Some of these solutions include StackGuard[1], ProPolice[2], and Microsoft's /GS compiler switch[5]. The shared premise of these solutions involves the placement of a cookie, or canary, between the buffers stored in a stack frame and the stack frame's return address. The cookie that is placed on the stack is used as a marker to detect if a buffer overflow has occurred prior to allowing a function to return. This simple concept can be very effective at making the exploitation of stack-based buffer overflows unreliable.

The cookie-based approach to detecting stack-based buffer overflows involves three general steps. First, a cookie that will be inserted into a function's stack frame must be generated. The approaches taken to generate cookies vary quite substantially, some having more implications than others. Once a cookie has been generated, it must be pushed onto the stack in the context of a function's prologue at execution time. This ensures that the cookie is placed before the return address (and perhaps other values) on the stack. Finally, a check must be added to a function's epilogue to make sure that the cookie that was stored in the stack frame is the value that it was initialized to in the function prologue. If an overflow of a stack-based buffer occurs, then it's likely that it will have overwritten the cookie stored after the buffer. When a mismatch is detected, steps can be taken to securely terminate the process in a way that will prevent exploitation.

The security of a cookie-based solution hinges on the fact that an attacker doesn't know, or is unable to generate, the cookie that is stored in a stack frame. Since it's impossible to guarantee in all situations that an attacker won't be able to generate the bytes that compose the value of a cookie, it really all boils down to the cookie being kept secret. If the cookie is not kept secret, then the presence of a cookie will provide no protection when it comes to exploiting a stack-based buffer overflow vulnerability. Additionally, if an attacker can trigger an exploitable condition before the cookie is checked, then it stands that the cookie will provide no protection. One example of this might include overwriting a function pointer on the stack that is called prior to returning from the function.

While the StackGuard and ProPolice implementations are interesting and useful, the author feels that no implementation is more critical than the one provided by Microsoft. The reason for this is the simple fact that the vast majority of all desktops, and a non-trivial number of servers, run applications compiled with Microsoft's Visual C compiler. Any one weakness found in the Microsoft's implementation could mean that a large number of applications are no longer protected against stack-based buffer overflows. In fact, there has been previous research that has pointed out flaws or limitations in Microsoft's implementation. For example, David Litchfield pointed out that even though stack cookies are present, it may still be possible to overwrite exception registration records on the stack which may be called before the function actually returns. This discovery was one of the reasons that Microsoft later introduced SafeSEH (which had its own set of issues)[6]. Similarly, Chris Ren et al from Cigital pointed out the potential implications of a function pointer being used in the path of the error handler for the case of a GS cookie mismatch occurring[9]. While not directly related to a particular flaw or limitation in GS, eEye has described some of the problems that come when secrets get leaked[3].

Even though these issues and limitations have existed, Microsoft's GS implementation at the time of this writing is considered by most to be secure. While this paper will not present a complete break of Microsoft's GS implementation, it will describe certain quirks and scenarios that may make it possible to reduce the amount of effective entropy that exists in the cookies that are generated. As with cryptography, any reduction of the entropy that exists in the GS cookie effectively makes it so there are fewer unknown portions of the cookie. This makes the cookie easier to guess by reducing the total number of possibilities. Beyond this, it is expected that additional research may find ways to further reduce the amount of entropy beyond that described in this document. One critical point that must be made is that since the current GS implementation is statically linked when binaries are compiled, any flaw that is found in the implementation will require a recompilation of all binaries affected by it. To help limit the scope, only the 32-bit version of GS will be analyzed, though it is thought that similar attacks may exist on the 64-bit version as well.

The structure of this paper is as follows. In chapter 3, a brief description of the Microsoft's current GS implementation will be given. Chapter 4 will describe some techniques that may be used to attack this implementation. Chapter 5 will provide experimental results from using the attacks that are described in chapter 4. Chapter 6 will discuss steps that could be taken to improve the current GS implementation. Finally, chapter 7 will discuss some areas where future work could be applied to further improve on the techniques described in this document.