Double Free

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:

Basic Information

If you free a block of memory more than once, it can mess up the allocator's data and open the door to attacks. Here's how it happens: when you free a block of memory, it goes back into a list of free chunks (e.g. the "fastbin"). If you free the same block twice in a row, the allocator detects this and throws an error. But if you free another chunk in between, the double-free check is bypassed, causing corruption.

Now, when you ask for new memory (using malloc), the allocator might give you a block that's been freed twice. This can lead to two different pointers pointing to the same memory location. If an attacker controls one of those pointers, they can change the contents of that memory, which can cause security issues or even allow them to execute code.

Example:

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

int main() {
    // Allocate memory for three chunks
    char *a = (char *)malloc(10);
    char *b = (char *)malloc(10);
    char *c = (char *)malloc(10);
    char *d = (char *)malloc(10);
    char *e = (char *)malloc(10);
    char *f = (char *)malloc(10);
    char *g = (char *)malloc(10);
    char *h = (char *)malloc(10);
    char *i = (char *)malloc(10);

    // Print initial memory addresses
    printf("Initial allocations:\n");
    printf("a: %p\n", (void *)a);
    printf("b: %p\n", (void *)b);
    printf("c: %p\n", (void *)c);
    printf("d: %p\n", (void *)d);
    printf("e: %p\n", (void *)e);
    printf("f: %p\n", (void *)f);
    printf("g: %p\n", (void *)g);
    printf("h: %p\n", (void *)h);
    printf("i: %p\n", (void *)i);

    // Fill tcache
    free(a);
    free(b);
    free(c);
    free(d);
    free(e);
    free(f);
    free(g);

    // Introduce double-free vulnerability in fast bin
    free(h);
    free(i);
    free(h);


    // Reallocate memory and print the addresses
    char *a1 = (char *)malloc(10);
    char *b1 = (char *)malloc(10);
    char *c1 = (char *)malloc(10);
    char *d1 = (char *)malloc(10);
    char *e1 = (char *)malloc(10);
    char *f1 = (char *)malloc(10);
    char *g1 = (char *)malloc(10);
    char *h1 = (char *)malloc(10);
    char *i1 = (char *)malloc(10);
    char *i2 = (char *)malloc(10);

    // Print initial memory addresses
    printf("After reallocations:\n");
    printf("a1: %p\n", (void *)a1);
    printf("b1: %p\n", (void *)b1);
    printf("c1: %p\n", (void *)c1);
    printf("d1: %p\n", (void *)d1);
    printf("e1: %p\n", (void *)e1);
    printf("f1: %p\n", (void *)f1);
    printf("g1: %p\n", (void *)g1);
    printf("h1: %p\n", (void *)h1);
    printf("i1: %p\n", (void *)i1);
    printf("i2: %p\n", (void *)i1);

    return 0;
}

In this example, after filling the tcache with several freed chunks, the code frees chunk h, then chunk i, and then h again, causing a double-free error. This opens the possibility of receiving overlapping memory addresses when reallocating, meaning two or more pointers can point to the same memory location. Manipulating data through one pointer can then affect the other, creating a critical security risk and potential for exploitation.

Executing it, note how i1 and i2 got the same address:

Initial allocations:
a: 0xaaab0f0c22a0
b: 0xaaab0f0c22c0
c: 0xaaab0f0c22e0
d: 0xaaab0f0c2300
e: 0xaaab0f0c2320
f: 0xaaab0f0c2340
g: 0xaaab0f0c2360
h: 0xaaab0f0c2380
i: 0xaaab0f0c23a0
After reallocations:
a1: 0xaaab0f0c2360
b1: 0xaaab0f0c2340
c1: 0xaaab0f0c2320
d1: 0xaaab0f0c2300
e1: 0xaaab0f0c22e0
f1: 0xaaab0f0c22c0
g1: 0xaaab0f0c22a0
h1: 0xaaab0f0c2380
i1: 0xaaab0f0c23a0
i2: 0xaaab0f0c23a0

References

Learn AWS hacking from zero to hero with htARTE (HackTricks AWS Red Team Expert)!

Other ways to support HackTricks:

Last updated