Search code examples
cmallocdynamic-memory-allocationfreeundefined-behavior

Is there a way to reliably malloc the same block of memory as a previously freed block, then access the content that was previously in it?


I have the following C program which requests some memory (str1), reads the content of a file into that space then frees it. Next, a block of the same size (str2) is requested, and the content is printed to stdout.

What I want is for str2 to contain the content of str1 so that the output is always the content of the file.

I am aware that what I am doing is undefined behaviour, in that I can't guarantee what the content of memory that has been allocated will contain. However, I'm trying to do some underhanded stuff for a demonstration where data from a file can be exfiltrated without it being obvious in a code review.

Almost all the time, I receive a block of memory at the same address for both str1 and str2, and most of the time when I run the program on macOS and Windows, the content of the file is printed. It seems to never happen on Linux (on Linux, calling free() seems to zero out the memory block).

Is there a way of making this more reliable on Windows and macOS, and is there any explanation for why it doesn't work at all on Linux?

My code is:

#include <stdlib.h>
#include <stdio.h>

int main() {
  FILE *file = fopen("data.txt", "r");
  char *str1 = malloc(4096*sizeof(char));
  fread(str1, 1, 4096, f);
  free(str1);
  
  char *str2 = malloc(4096);
  printf("Content: %s\n", str2);
  free(str2);
}

Solution

  • Essentially, what happens when you allocate and free is a black box to you. There is absolutely no reliable way to get the same address. Calling free means that you tell the OS that you're done with the memory, and there's no undo for this.

    What I want is for str2 to contain the content of str1 so that the output is always the content of the file.

    You basically have three options here.

    1. Wait with the call to free
    2. Copy the buffer before you call free
    3. Write your very own implementation of malloc and free

    From comments:

    imagine that I'm going to allocate some memory then read something sensitive (e.g. a private key) into that block and do something with it. Later, I allocate some memory of the same size and stick some data into it that will be saved to a file. If I don't overwrite all the data in the block then it may contain some sensitive info that would get saved to the file. In that case, it may not be obvious from a code review that some sensitive data exfiltration is possible. I want to demonstrate that sensitive data can be exfiltrated in a non-obvious manner.

    Nice thing, but these kind of exploits almost always relies on undefined behavior. As you say yourself, it's a security concern. So there's really no point in providing a reliable way to do this.

    Here is a snippet that worked for me on Fedora Linux.

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    int main(void) {
        char *s = malloc(100);
        const char str[] = "Hello, World. Prepare to meet your doom.";
        strcpy(s, str);
        free(s);
        for(int i=0; i<strlen(str); i++) 
            putchar(s[i]);
        puts("");
    }
    

    My output:

    $ ./a.out 
    ��epare to meet your doom.
    

    As you can see, I got parts of the data, but not all. And to demonstrate the undefined behavior of this, here is output with different optimizations:

    $ gcc k.c -O1
    $ ./a.out 
    0Separe to meet your doom.
    $ gcc k.c -O2
    $ ./a.out 
    @�
    $ gcc k.c -O3
    $ ./a.out 
    �
    

    Your method is very unreliable for this, because you will print until the first 0 in the string. Here is code that will output nothing, which can fool you that the data has been wiped. That's why I used putchar in a loop above.

    char str[] = "Hello, World";
    str[0] = '\0';
    printf("%s", str); // Will print nothing, but only first character is wiped