GS


/GS

The /GS flag has been around since the release of Visual Studio 7.0. In the original approach, it was simply a randomly generated cookie placed between the return address and the local variables on the stack. If the EBP register was pushed onto the stack, the cookie would guard this as well. It would stop some of the simplest attacks against simple functions, and, although it was better than nothing, no developer should ever believe that they’re protected against attacks merely because of /GS. Let’s take a look at a sample application:

 #define _CRT_SECURE_NO_DEPRECATE #include <stdio.h> #include <string.h> void VulnerableFunc( const char* input, char* out ) {    char* pTmp;    char buf[256];    strcpy( buf, "Prefix:" );    strcat( buf, input );    // Transform the input, and write it to the output buffer    for( pTmp = buf; *pTmp != '\0'; pTmp++ )    {         *out++ = *pTmp + 0x20;         }    }     int main( int argc, char* argv[] )    {        char buf2[256];        VulnerableFunc( argv[1], buf2 );        printf( "%s\n", buf2 );        return 0;    }

Starting just after we enter main, here’s the commented disassembly (my comments above the instructions):

 int main( int argc, char* argv[] ) { // Save the previous value of the frame pointer on the stack 00411300 push        ebp // Put the current stack pointer into the ebp register 00411301 mov         ebp,esp // Create room for buf2 00411303 sub         esp,140h // Save three more registers on the stack 00411309 push        ebx 0041130A push        esi 0041130B push        edi    char buf2[256];    VulnerableFunc( argv[1], buf2 ); // Get the address of buf2, and put it on the stack 0041130C lea         eax,[buf2] 00411312 push        eax // Find the value of argv[1], also place it on the stack 00411313 mov         ecx,dword ptr [argv] 00411316 mov         edx,dword ptr [ecx+4] 00411319 push        edx // Call VulnerableFunc, and on return, adjust the stack pointer 0041131A call        VulnerableFunc (41102Dh) 0041131F add          esp,8

Once inside VulnerableFunc, we have this:

 void VulnerableFunc( const char* input, char* out ) { // Save the previous   value of the frame pointer on the stack 00411260 push          ebp // Put the current stack pointer into the ebp register 00411261 mov           ebp,esp // Create room for buf and pTmp 00411263 sub           esp,144h 00411269 push          ebx 0041126A push          esi 0041126B push          edi     char* pTmp;     char buf[256];

In this example, all optimizations and stack checking are disabled so that you can see what typical applications used to do. In this application, the stack is shown by the diagram in Figure 3-2.

image from book
Figure 3-2: Stack diagram for a typical application.

Now let’s look at what happens, step by step, as buf overflows. The first value to get mangled is pTmp, but that won’t get us anywhere–it will just be initialized to zero and incremented as we process the buf character array. The next value to get overwritten is the previous frame pointer. This is interesting because on return from the second function (in this case, main), code execution will jump into the address located here. If we had the classic off-by-one attack, this is where the fun starts.

Next, things start to get really fun: we can now overwrite the return address and set the input and out values to anything we like. Setting input isn’t especially interesting–by the time the overrun has happened, we’ve already used that variable and won’t write it again. The out parameter is a real problem, because once that is overwritten the attacker can write nearly anywhere in application memory that they’d like once we fall into the for loop toward the end of VulnerableFunc.

In the original /GS implementation, a cookie was just placed between the local variables and the stored registers (if any) and the return address. This would leave pTmp open to attack, and although in this case, it is uninteresting, that may not always be true, especially if this were a function pointer. In the case of a limited overwrite, it isn’t possible to tamper with the previous frame pointer without detection, so /GS in its simplest incarnation would stop a simple off-by-one (or even off-by-a-few) overflow from being exploitable. In the case of an unchecked overrun, the attacker has the problem of having to fix up the cookie, but recall that overwriting the out pointer allows the attacker to put nearly any value he or she would like into any memory location (although in this example, a location with any 0 bytes would be a problem). Let’s take a look at what the most recent version of /GS will do with the same function:

 void VulnerableFunc( const char* input, char* out ) { // Make room for the local variables 00401000 sub        esp,104h // Copy the security cookie into eax 00401006 mov        eax,dword ptr [___security_cookie (403000h)] // XOR the cookie with the stack pointer 0040100B xor        eax,esp // Put the resulting cookie at the end of the buffer 0040100D mov        dword ptr [esp+100h],eax      char* pTmp;      char buf[256];            strcpy( buf, "Prefix:" ); 00401014 mov        ecx,dword ptr [string "Prefix:" (4020DCh)] // Now copy the function arguments into registers before anything can // tamper with them. 0040101A mov        eax,dword ptr [esp+108h] 00401021 mov        edx,dword ptr [esp+10Ch]

As you can see, this implementation is much safer. The arguments to the function are either copied into registers or are put on the stack above the buffer. Additionally, as we discuss in the next section, if an exception were thrown inside this function, the cookie would be checked prior to executing the exception handler.

Note 

While you should always use /GS, you still need to write solid code!



Writing Secure Code for Windows Vista
Writing Secure Code for Windows Vista (Best Practices (Microsoft))
ISBN: 0735623937
EAN: 2147483647
Year: 2004
Pages: 122

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net