One rule of thumb in c/c++ is that you should never let the user be in control of a formatting string. This has been recognized as a security bug for years, and one that has been mostly cleaned up since it is so easy to identify and fix. With visual studio 2005, Microsoft released a safer version of the crt -- functions that end with _s to tell you that they are security enhanced. So let's say you are being a good security citizen by using the safe-crt .... can a format string vulnerability (where the user controls the format string) still be exploited?
The MSDN docs don't really have the answer. A cursory reading of the "security enhancements in the CRT" page as well as others may lead you to believe that format string vulnerabilities are a thing of the past. One example shows a call to 'sprintf_s(buf,_countof(buf), "%s",NULL)' and remarks that this results in a runtime error. Looks like they do some kind runtime-validation. However, unless they added magic pixie dust to their compiler that sends cosmic rays from outer space to fix up malicious format strings at runtime, it isn't really possible to have strongly-typed printf-style format strings in C.
So let's investigate how far the parameter validation will get you. Here is a little sample program I wrote to send nasty format strings to sprintf_s:
#include
#define OUT_SIZE 0x1000
int main(int argc, char** argv) {
char * out = new char[OUT_SIZE];
sprintf_s(out, OUT_SIZE, OUT_SIZE, argv[1]);
printf("%s\n", out);
return 0;
}
So let's try this with a couple of format strings:
Input: "%s"
Output: Error: ("Buffer too small", 0)
So far so good... but buffer too small?
What about just dumping stack variables?
Input: "%p %p %p %p %p %p"
Output: 00344FD0 00344FD0 0012FFB8 004019D3 00000002 00343728
Interesting... so looks like this type checking is not so robust after all. We've just dumped the stack.
Let's see if we can crash the program. Looks like there is a 0000002 on the stack... that probably won't appreciate being dereferenced.
Input: "%p %p %p %p %s"
Ok so we can crash the program. Can we do anything more interesting?
Let's say there was some interesting data somewhere in the program. To simulate this, I'll put my bank account number on the stack with the following line of code at the beginning of "main" "volatile char * bankAccount = "Account#123-456-7890";" (the volatile helps convince the compiler not to throw it away since I don't use it).
Now when I call the function with the right input, I can dump my bank account number:
Input: test.exe "%p %s"
Output: 00344FD0 Account#123-456-7890 00344FD0
Ok but nobody really cares about Denial-of-service and Information-disclosure. Those are sooooo pri-3. Can we use take over the machine? As everyone knows, the hacker's favorite format string character is '%n'. '%n' writes the number of bytes written so far to the param from the stack. Let's try a '%n':
Input: test.exe "%p %n"
Output: Error: (state != ST_INVALID)
Blast! Foiled! It turns out Microsoft decided that %n was too much power, and that we mere mortals couldn't handle it. Good for them. There is an override, but it turns out to not be available using the Safe CRT. The moral of the story? The safe crt is a wonderful and powerful tool to help prevent buffer overruns. But there is no excuse for letting a user control a format string.
Trackback URL for this post:
http://www.casabasecurity.com/trackback/34
Comments
Post new comment