Disclaimer: This is not an all encompassing tutorial on use after frees, but rather notes and code that I used to better understand the bug class.

A common bug class in real world targets are Use After Frees (aka UAF). In programs written in C or C++ where memory management is controlled by the programmer it is critical that proper clean up is done on objects that have been freed. When clean up is not done properly it can result in crashes, use of unexpected values or even code execution [1]

To better understand UAFs I wrote a simple C program that demonstrates a common UAF scenario and how one could go about exploiting it.

Full Code

In this example we have a struct called person that has these properties:

struct person { 
    int age;
    char *name;
    void (*func_ptr)(struct person*);
};

This person object is going to be our object that will be used after freed, it contains an integer that will store the person’s age, a char pointer that will point to the persons name, and a function pointer that will be set to a function that prints the persons name. We initialize the person using the following code:

void print_name(struct person *ptr)
{
    printf("Your Person's name: %s\n",ptr->name);
    return;
}

//Allocate our person
struct person *my_person = (struct person*) malloc(sizeof(struct person));

//Fill out struct information
my_person->age = 23;
my_person->name = malloc(7);
strncpy(my_person->name,"Robert\x00",7);
my_person->func_ptr = &print_name;

We can see that we now have an object called my_person with the name: Robert, age: 23, and func_ptr set to print_name. We can call the function print_name like my_person->func_ptr(my_person);

In C we can print out somethings that will be useful for debugging and exploitation such as the address that is returned from malloc and the size of the object. We can also execute our func_ptr and get normal behavior of printing out our name.

printf("Addr of person: 0x%x\n", my_person);
// Prints: Addr of person: 0x5655a1a0

printf("Size of object: %d\n", sizeof(struct person));
// Prints: Size of object: 12

//Execute the function pointer that will print our person's name
my_person->func_ptr(my_person);
// Prints: Your Person's name: Robert

Now that we have initialized my_person and printed useful information we will free the object but not clear out the pointer. Depending on the heap allocator that is used will determine what happens when we free an object. This program was run on linux and used glibc for allocation so free will only change meta data of the heap object to indicate that it is freed. It will not clear out the contents of my_person on the heap. In GDB we can print examine the heap and see what the object looks like:

                                                (addr of print_name)
0x5655a1a0:     0x00000000      0x00000000      0x5655626d      0x00000011
0x5655a1b0:     0x65626f52      0x00007472

Lets free our person now.

//Free person but keep ptr to it 
free(my_person);

Now that our person object is freed but not cleaned up our goal is to allocate something that will fill the same memory location @ 0x5655a1a0 with controlled data. This will allow us to then use my_persons func_ptr call to execute malicious code. For this demonstration I included a struct called blob with the following definition:

struct blob {
    char data[12];
};
struct blob *my_blob = (struct blob*) malloc(sizeof(struct blob));

This blob object is based of a real world example of a Chrome Sandbox escape exploit blog post written by Theori [2]. In Chrome you can allocate objects called blobs with an user supplied size that will act like a data store. To recreate that functionality in my C program I just created a struct with a char array the same size as our person. The reason for this is so that the likely hood that when I call malloc it returns the same pointer of our now freed person is greater. (first fit) In many real world applications you may need to spray objects as the first allocation will not always fill up the freed object. In the chrome escape and our toy program the first allocation after free conveniently does fill the freed objects space.

To confirm that we are in the same location we can look into the data with my_blob->data and see if we can see our name “Robert” there. In real world examples many people put magic values in objects so they can check if they have the right objects.

//Loop through our blob to see if we are in the 
//same location as my_person was in
for(int i = 0; i < 20; i++) {
    if(my_blob->data[i] == 'R' && my_blob->data[i+1] == 'o'){
        puts("We replaced the freed object with a blob!");
        replaced = true;
    }
}
//We can also just print the address to make sure
printf("Addr of blob: 0x%x\n", my_blob);
// Prints: Addr of blob: 0x5655a1a0

Now that we know we have a blob object in place of the freed person object we can change the previous function pointer that calls our print_name function. For demonstration purposes we included a function called win that simply calls system("/bin/sh"). After we overwrite the function pointer using the blob we can then use the my_person object again to call func_ptr

void win() 
{
    puts("You win");
    system("/bin/sh");
}

//Use our blob to overwrite the func_ptr to win
my_blob->data[8] = &win;

//Use the ptr after it has been freed and now call system!
my_person->func_ptr(my_person); 

If we had nulled out the my_person ptr after freeing it we would not be able to call the func_ptr again and therefore not be able to exploit this example.

In summary when bug hunting it is good to look for UAF patterns. Understanding of what objects are allocated, where and why are they freed and if there are any code paths that result in object pointers being freed but still used after it has been freed. If objects are still used after being freed we can look into what data that object contains and if there are other objects that we can control to replace that freed object. Good targets are function pointers or replacing with objects that will leak information like addresses to bypass ASLR.

References

  1. CWE-416: Use After Free
  2. Cleanly Escaping the Chrome Sandbox