xorl %eax, %eax

Manipulating Uninitialized Variables

leave a comment »

This is not a new concept, however, numerous times simple uninitialized variables bugs are treated as non-security related bugs or as denial of service only vulnerabilities. In most cases, those vulnerabilities are perfectly exploitable conditions if the attacker is able to manipulate the stack layout (considering the most common case, of uninitialized variables on the stack segment). Here is a dummy example application:

#include <stdio.h>
#include <string.h>

void
do_stuff2(int n)
{
    int i; 
    size_t len; 
    short tmp;
    printf("i   = %#x (%d)\n"
           "len = %#x (%d)\n"
           "tmp = %#x (%d)\n"
           , i,i, len,len, tmp,tmp);
}

void
do_stuff(char *c)
{
    char buf[30];
    memset(buf, *c, sizeof(buf)-1);
    buf[sizeof(buf)-1] = 0;
    printf("Set buffer to: '%s'\n",    buf);
}

int
main(int argc, char *argv[])
{
    if (argc == 2)
        do_stuff(argv[1]);
    else
        do_stuff("");
    do_stuff2(0);
    return 0;
}

You can see that function do_stuff2() uses three uninitialized variables (i, len and tmp). Of course, here nothing critical happens with these three variables but you can assume that some applications might try to use an uninitialized variable as a counter or a size limit to a memcpy() or similar calls. Function do_stuff() just fills in a buffer and then prints it out with user’s first argument if it is given. At first you might not see how we can manipulate the three variables of do_stuff2() function but if you consider about the creation and management of the stack frames you will quickly end up with a really straightforward exploitable case. Assume that the user does not provide any arguments, then the initial stack frame for the first function would be something similar to:

--------------------
   arg to func: ""
--------------------
  function prologue
--------------------
  allocate space 
    for buf[30]
--------------------
    memset args
--------------------
    printf args
--------------------
  function epilogue
--------------------

Using GCC 4.2.3 the allocated space was 40 bytes (0x28) as you can see after the function prologue using a debugger:

0x080483b2 <do_stuff+0>:        push   %ebp
0x080483b3 <do_stuff+1>:        mov    %esp,%ebp
0x080483b5 <do_stuff+3>:        sub    $0x28,%esp

When this function reaches the function epilogue the stack pointer (ESP) and the base pointer (EBP) get restored and the execution is transfered back to the position of initial call at main() function. Now, when the second call takes place, the call to do_stuff2() the stack frame is this:

--------------------
   arg to func: 0
--------------------
  function prologue
--------------------
  allocate space 
  for i, len, tmp
--------------------
    printf args
--------------------
  function epilogue
--------------------

Here, GCC allocates 24 (0x18) bytes as you can see at the following disassembly:

0x08048384 <do_stuff2+0>:       push   %ebp
0x08048385 <do_stuff2+1>:       mov    %esp,%ebp
0x08048387 <do_stuff2+3>:       sub    $0x18,%esp

As you already know, the stack is not cleaned between the two calls, thus the data written to the buffer at the first call (do_stuff()) remain at the stack where the second stack frame allocates space to store the three uninitialized variables. This means that those three variables get whatever values they have at their range directly from the stack. Since we control the buffer of the first call we can manipulate the stack to contain whatever we want and when the next call will use this space for its local variables it will get whatever we have left there. This way we can have completely predictable values to these uninitialized variables, and this is the reason why in most cases this class of vulnerability is exploitable if the attacker can perform simple operations to the vulnerable application. Here is the demonstration of the above:

sh-3.2$ gcc un.c -o un
sh-3.2$ ./un
Set buffer to: ''
i   = 0 (0)
len = 0 (0)
tmp = 0x800 (2048)
sh-3.2$ 

Everything seems to be far away from our control, but consider the above and let’s fill the buffer with ‘X’ (0x58) and see what happens:

sh-3.2$ ./un X
Set buffer to: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
i   = 0x58585858 (1482184792)
len = 0x58585858 (1482184792)
tmp = 0x58 (88)
sh-3.2$ 

Yeap! Exactly as we expected. Do you still believe that most uninitialized variable bugs are just denial of service or even not security related bugs? Imagine situations where uninitialized variables are used as counters, size limits, pointers to control structures etc. Just don’t be lazy and add a few lines of code to initialize your variables :p

Written by xorl

January 6, 2009 at 22:04

Posted in C programming

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s