Thursday, January 26, 2012

Quick Format String Vulnerability Check for the Lazy Hacker

Hello guys, it's about time we did some assembly. I decided to wrap up a quick blog post in 30 minutes, so here is one:

I will discuss a quick and dirty method that can be easily automated to check for format string vulnerabilities. I will assume you already know what buffer overflow and format string vulnerabilities are.

Assume this very very vulnerable code:

#include <iostream>
int main()
{

    char x[100];
    printf("%s","Enter your string:\n");
    std::cin.get(x, 99);
    printf(x); //WARNING: BAD VULNERABLE CODE, TEST ONLY
   
    return 0;
}

This code obviously suffers from a format string vulnerability. However, if you don't have the source code, how can you quickly tell (not 100 percent accurate, but a very quick indicator nonetheless) ?

By checking stack adjustment after the call to printf. Let's dive to assembly:

8:        printf(x);
00401510   lea         ecx,[ebp-64h]
00401513   push        ecx
00401514   call        printf (0041f150)
00401519   add         esp,4
9:
10:       return 0;
0040151C   xor         eax,eax
11:   }

After the call to printf(x), the stack was adjusted by 4 bytes (add esp,4) , why? Because of this:

int printf ( const char * format, ... );

The function printf can accept one parameter, but ideally it should be two: The format string itself and the variables corresponding to each placeholder. For example printf("%d",4) where "%d" is the format string and 4 is the variable corresponding to the %d inside the format string.

If we call printf("Hello") then we are only pushing one set of 4-bytes, a DWORD (in our past example the register ecx).

So what if we changed printf(x) to printf("%s",x)? Let's look at the dissassembly.

8:        printf("%s",x);
00401510   lea         ecx,[ebp-64h]
00401513   push        ecx
00401514   push        offset string "%s" (0046c01c)
00401519   call        printf (0041f150)
0040151E   add         esp,8

As we can see, the stack was adjusted by 8 bytes, or two DWORDs. What we pushed was the address of x (in ecx) and the address of "%s" to the function.

When we only called printf(x) we pushed the address of x stored in ecx, so only 4 bytes were pushed in the vulnerable call so only 4 bytes had to be corrected for the stack pointer (ESP). In the correct call we pushed 8 bytes and hence had to correct the stack pointer (ESP) by 8 bytes.

Very simple, no? The great thing about this is the extreme ease which which you can automate the search for such vulnerable usage in any binary with any tool you want: IDA Pro, PyDASM...etc

This trick is very simple and not complicated in any form, but it can really speed up the search for format string vulnerabilities. Enjoy!

No comments:

Post a Comment