Taking Buffer Overruns Seriously

Today's reports about yet another virus worm, again based on exploiting buffer overruns underscores the importance of understanding and removing these problems from your and your company's code. This particular one occured yet again in a Microsoft product but buffer overruns are and have been exploited in almost every operating system or software product based on C/C++. The question is what can be done about this? Well, if you are developing C++ based software on Windows, you must seriously consider recompiling alll your code with the /GS flag of VC++.NET 2003 and then give some consideration to Windows Server 2003.

What are buffer overruns? They are pretty simple and well known. A buffer overrun is simply the ability to corrupt memory arbitrarly by writing in memory next to some buffer. What is in that buffer can be exploited through various means. Its very easy for a C++ programmer to create a buffer overrun. Witness:

 

int someValue = 2;

char buffer[10];

strcpy(buffer. input);

printf("someValue = %i\n", someValue);

 

If I pass in any string greater than 11 characters, I have a buffer overrun and have corrupted memory. Many C++ programmers get that this bad code and screws up memory. Many do not get that this is also a security hole waiting to be exploited. Overwriting the stack like this with a buffer overrun can overwrite the return address, frame pointer, and EH frame of an X86 stack. There are all sorts of stack exploits that can be done. See Writing Secure Code for comprehensive coverage.

These problems don't exist (not in this fashion) with managed environments like Java and .NET. While "safe" string copying functions are being added to the C++ standard, what can existing native C and C++ programmers do? On the Microsoft platform, VC++ has had Run Time Checks since VC++ 6. The /RTC flags perform various runtime checks. These are not sufficient to detect buffer overruns and they require debug builds, hardly any help in production code. VC++ .NET 2002 introduced the /GS switch which allows the compiler to inject code to detect buffer overruns and prevent hijacking of the return address. This was a big step in the right direction and Windows XP, the .NET Framework, and many Microsoft products were compiled with it. Code recompiled with /GS will show a dialog when a buffer overrun is detected.

I covered the /GS switch in my O'Reilly story on What's New in Visual C++ .NET 2003? The /GS switch is not totally new but is enhanced in the Everett C++ compiler. The details are a little geeky but in essence, if you look at the x86 code in the generated function prolog compiled with /GS it used to have a cookie which took a value from a global variable and XOR'd it with the return address. When the function ends, it checks to see if that cookie is changed on the stack. In Everett C++, the cookie is no longer an XOR of the global value and the return address as this was not effective in many attacks. Also, the stack is optimized such that local buffers are allocated before local variables, getting them out of the way of buffer overrun attacks. However, since adding these instructions can cost performance, they are now put in only for "vulnerable functions", which are  functions that contain buffers that look like string buffers (each element is either 1 or 2 bytes and buffer has 4 or more bytes of storage). I won't go into the new cookie generation algorithm as I am not sure if this is public information.

So you are asking, "what is this going to cost me in performance?" The security check is 9 x86 instructions. With VC++ 7.1 (Everett) and its performance optimizations, most apps do not notce a thing. The average is 2%.

So the idea here with security checks is that buffer overruns are your fault. They are bugs in your code that can be exploited and every single one of them should be viewed as a target. /GS helps find ones that you missed but does not stop you from writing shitty code. Shitty code is shitty code and should be dealt with accordingly. This kind of code should never be tolerated or excused.

In addition, Windows 2003, includes platform features to help defeat buffer overruns and attacks.

What does this have to do with today's attack? Microsoft will have a long way to go in recompiling millions of lines of existing code with /GS and the transition to managed code. It won't happen overnight. And all of our C++ code is just as vulnerable. If you are a native C++ programmer, make sure to take advantage of Visual C++ .NET 2003 when it ships later this spring and make sure you don't write your C++ code this way.