FREE Subscription to Dr. Dobb’s Digest: Same Great Content, New Digital Edition
Site Archive (Complete)
Security
Email
Print
Reprint

add to:
Del.icio.us
Digg
Google
Furl
Slashdot
Y! MyWeb
Blink
February 23, 2004
Windows Stack Buffer Overflow Protection

Jason Coombs
Using a canary and Visual C++ 7.0's /GS compiler option can help detect security compromises
Windows Stack Buffer Overflow Protection


In "Backdoors Can Damage Trust" (August 20, 2003, http://www.windevnet.com/documents/s=8854/win1061417316858/), I looked at a simple stack buffer overflow that could result in a program crash or DoS condition but that to a knowledgeable attacker would also be exploitable to execute arbitrary code. The vulnerability is exploited by exceeding the dimensional boundary of a stack-allocated buffer, thereby modifying the function return address that is popped off the stack when the microprocessor encounters a return instruction. Stuffing malicious bytes into stack memory through such an exploit is messy. Every byte of memory beginning with the starting address of the vulnerable stack buffer is overwritten with malicious bytes that have just the right length and structure to drop a replacement pointer value onto the authentic stack frame EIP return address in order to take control of the instruction pointer when the compromised stack frame exits with RET.

The replacement of everything in between the memory buffer and the stack frame return address leaves evidence of a buffer overflow condition. We can detect stack buffer overflows at runtime by placing a canary on the stack near each frame's boundary and RET address pointer. (The canary, of course refers to the coal miner's canary, which would die quickly in the presence of odorless toxic fumes, thus alerting the miner, who could then exit the mine for a smoke break.) The canary created by Visual C++ 7.0 is referred to by Microsoft as a “cookie”—a token of unpredictable value selected at runtime that can be used to confirm that the authentic return address has not been modified before the microprocessor is allowed to trust it and pop it off the stack. Consider the following sample code:

int main(int argc, char* argv[]) {
void * p[2] = {(void *)p[3],(void *)p[4]};
char unchecked[13];
p[3] = (void *)&p[0];
p[4] = (void *)0x00411DB6;
printf(strcpy(unchecked,"Hello World!\n"));
return 0; }

This code, when compiled with /GS in Visual C++ 7.0, results in a stack buffer overflow condition when p[4] is set equal to the address of the original call to the main function (located in my test build at 0x00411DB6). This results in the death of the canary, and program execution terminates abruptly. Figure 1 shows the security cookie being retrieved into the EAX register. This cookie value was generated dynamically by the C runtime prolog code and stored in memory location 0x00425B40 where the compiled code expects to find it at runtime. The security cookie is not the canary, it is the pseudorandom encryption key used to produce the canary through the very next instruction involving exclusive or (xor).

Figure 1: The guard stack security cookie retrieved into EAX.

What happens next is the birth of the canary, or its placement into the cage, to extend the metaphor. The canary is the bitwise xor combination of the security cookie and the authentic return address to which the current call stack expects program execution to return to when the subroutine completes. Figure 2 shows the canary, which is the value 42FC11E7 stored from EAX into the four bytes just below the previous stack frame base address that was pushed onto the stack in the very first instruction at the beginning of the main function (push ebp). The four-byte canary is placed in this location because it is the last four-byte region of the new stack frame. Any buffer overflow exploits that impact the stack frame will have to write through the canary to get at the return address, which is stored in the four bytes of memory beginning four bytes above the base address of the new stack frame. Between the canary and the return address are the four bytes containing the previous stack frame base address 0012FFC0. The authentic return address shown in Figure 2 is 00411DBB that you can see on the line addressed beginning at 0x0012FEDC, which happens to be the current value of EBP—the current stack frame base address.

Figure 2: An electronic canary 42FC11E7 on the stack.

This sample's mission is to capture the previous stack frame base address into the first element of void pointer array p and then take control of the return address to which the present stack frame will jump when the RET instruction is executed. Figure 3 shows these steps being carried out as planned. The hard-coded address 0x00411DB6 is the address the sample prefers instead of the authentic return address, and you can see the mov instruction at the top of the disassembly shown in Figure 3 that forces this new value in place of the authentic original. The authentic address of the previous stack frame base has been overwritten by this time. Both of these malicious replacement values are now present on the stack and appear in the Memory 1 box beginning at line 0x0012FEDC. The next instruction to be executed, marked by the arrow and the address shown in EIP, moves the canary into the ECX register where it can be examined.

Figure 3: Our sample overflow code hard at work.

You can see in Figure 4 that the canary is still alive in its cage located at the very top of the stack frame where it was first placed, as shown in Figure 3. Or is it? The canary value hasn't changed, but the return pointer has. Another quick xor using the new return address and the same security cookie as used previously, and we can take the pulse of the canary to see if it's really alive. Figure 4 shows the result.

Figure 4: The canary looks fine until it fails the security check.

The ECX register contains a value other than the security cookie and the canary is shown to have died. Of old age, perhaps, since it's now outdated and doesn't confirm the authenticity of the return address that program execution is about to be handed over to when the return instruction is encountered. The __security_check_cookie runtime library function calls ExitProcess when it detects the security compromise. Without the /GS canary on the stack, there is no validation of the stored stack frame return pointer value prior to its use.


Jason Coombs works as forensic analyst and expert witness in court cases involving digital evidence. Information security and network programming are his areas of special expertise. He can be reached at jasonc@science.org.

TOP 5 ARTICLES
No Top Articles.



MICROSITES
FEATURED TOPIC

ADDITIONAL TOPICS

INFO-LINK