Double Free
This technique is used as part or a exploit technique
Metadata corruption
heap chunk
1
2
3
4
5
6
7
8
9
10
11
struct malloc_chunk {
INTERNAL_SIZE_T mchunk_prev_size; /* Size of previous chunk, if it is free. */
INTERNAL_SIZE_T mchunk_size; /* Size in bytes, including overhead. */
struct malloc_chunk* fd; /* double links -- used only if this chunk is free. */
struct malloc_chunk* bk;
/* Only used for large blocks: pointer to next larger size. */
struct malloc_chunk* fd_nextsize; /* double links -- used only if this chunk is free. */
struct malloc_chunk* bk_nextsize;
};
typedef struct malloc_chunk* mchunkptr;
in double free we attempt to control fd
. By overwriting it with our desired memory address, we can tell malloc()
where the next chunk is to be allocated. Example : if we overwrote fd
of a
with 0xdeadbeef
; one a
is free, the next chunk on the list will be 0xdeadbeef
.
Controlling fd
Vulnerable code
class="highlight">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <stdio.h>
#include <stdlib.h>
int main() {
char *a, *b, *c;
a = malloc(8);
b = malloc(8);
printf("a ptr : %p\nb ptr : %p\n", a, b);
free(a);
free(b);
free(a);
printf("Double free of a performend with b in between to bypass \"(double free or corruption) fasttop protection\"\n");
c = malloc(8);
printf("c ptr : %p\n", c);
return 0;
}
class="highlight">1
2
3
4
5
./chall
a ptr : 0x804b008
b ptr : 0x804b018
Double free of a performend with b in between to bypass "(double free or corruption) fasttop protection"
c ptr : 0x804b008
a is both freed and in use
The heap attempts to save as much space as possible and when the chunk is free the fd
pointer is written where the user data used to be.
Allocated
class="highlight">1
2
3
4
5
struct malloc_allocated_chunk {
INTERNAL_SIZE_T mchunk_prev_size;
INTERNAL_SIZE_T mchunk_size;
void data; // mchunk_size
}
Freed
class="highlight">1
2
3
4
5
6
struct malloc_free_chunk {
INTERNAL_SIZE_T mchunk_prev_size;
INTERNAL_SIZE_T mchunk_size;
struct malloc_chunk* fd; // to next free chunk in list
struct malloc_chunk* bk; // prev free chunk in list
}
So when we write into the use data of c
we are writing into the fd
of a
at the same time, and this means we control where the next chunks gets allocated.
Exploiting
steps
- allocate 2 ptrs
- free ptr a, then b, then a again
- allocate ptr a (with fake metadata (addr))
- allocate 2 chunks a, b
- allocate 1 more (overwrite)
User data corruption
Vulnerable code
class="highlight">1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdio.h>
#include <stdlib.h>
int main() {
char *a, *b, *c, *d, *e, *f;
a = malloc(8);
b = malloc(8);
c = malloc(8);
printf("a PTR : %p\n", a);
printf("b PTR : %p\n", b);
printf("c PTR : %p\n", c);
free(a);
free(b);
free(a);
printf("double free a and b in between to bypass \"double free or corruption (fasttop protection)\" \n");
d = malloc(8);
e = malloc(8);
f = malloc(8);
printf("d PTR : %p\n", d);
printf("e PTR : %p\n", e);
printf("f PTR : %p\n", f);
return 0;
}
Output
class="highlight">1
2
3
4
5
6
7
8
./chall
a PTR : 0x804b008
b PTR : 0x804b018
c PTR : 0x804b028
double free a and b in between to bypass "double free or corruption (fasttop protection)"
d PTR : 0x804b008
e PTR : 0x804b018
f PTR : 0x804b008
Notice how d and f pointers point to the same memory address. any changes in one will affect the other
What happened
class="highlight">1
2
3
4
5
6
7
8
9
10
11
12
1. a is freed
head -> a -> tail | fastbin
2. b is freed
head -> b -> a -> tail | fastbin
3. a is freed again
head -> a -> b -> a -> tail | fastbin
4. malloc request for d
head -> b -> a -> tail | fastbin (a is returned)
5. malloc request for e
head -> a -> tail | fastbin (b is returned)
6. malloc request for f
head -> tail | fastbin (a is returned)
Trending Tags