|
2 | 2 | #include <stdlib.h>
|
3 | 3 | #include <assert.h>
|
4 | 4 |
|
| 5 | +/* |
| 6 | +Original reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8 |
| 7 | +
|
| 8 | +This document is mostly used to demonstrate malloc_consolidate and how it can be leveraged with a |
| 9 | +double free to gain two pointers to the same large-sized chunk, which is usually difficult to do |
| 10 | +directly due to the previnuse check. |
| 11 | +
|
| 12 | +malloc_consolidate(https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4714) essentially |
| 13 | +merges all fastbin chunks with their neighbors, puts them in the unsorted bin and merges them with top |
| 14 | +if possible. |
| 15 | +
|
| 16 | +As of glibc version 2.35 it is called only in the following five places: |
| 17 | +1. _int_malloc: A large sized chunk is being allocated (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3965) |
| 18 | +2. _int_malloc: No bins were found for a chunk and top is too small (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4394) |
| 19 | +3. _int_free: If the chunk size is >= FASTBIN_CONSOLIDATION_THRESHOLD (65536) (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L4674) |
| 20 | +4. mtrim: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5041) |
| 21 | +5. __libc_mallopt: Always (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L5463) |
| 22 | +
|
| 23 | +We will be targeting the first place, so we will need to allocate a chunk that does not belong in the |
| 24 | +small bin (since we are trying to get into the 'else' branch of this check: https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3901). |
| 25 | +This means our chunk will need to be of size >= 0x400 (it is thus large-sized). |
| 26 | +
|
| 27 | +*/ |
| 28 | + |
5 | 29 | int main() {
|
6 |
| - // reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8 |
7 |
| - puts("This is a powerful technique that bypasses the double free check in tcachebin."); |
8 |
| - printf("Fill up the tcache list to force the fastbin usage...\n"); |
| 30 | + printf("This technique will make use of malloc_consolidate and a double free to gain a UAF / duplication of a large-sized chunk\n"); |
9 | 31 |
|
10 | 32 | void* p1 = calloc(1,0x40);
|
11 | 33 |
|
12 |
| - printf("Allocate another chunk of the same size p1=%p \n", p1); |
13 |
| - printf("Freeing p1 will add this chunk to the fastbin list...\n\n"); |
14 |
| - free(p1); |
| 34 | + printf("Allocate a fastbin chunk p1=%p \n", p1); |
| 35 | + printf("Freeing p1 will add it to the fastbin.\n\n"); |
| 36 | + free(p1); |
| 37 | + |
| 38 | + void* p3 = malloc(0x400); |
| 39 | + |
| 40 | + printf("To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\n"); |
| 41 | + printf("which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\n"); |
| 42 | + printf("a chunk with chunk size 0x410. p3=%p\n", p3); |
15 | 43 |
|
16 |
| - void* p3 = malloc(0x400); |
17 |
| - printf("Allocating a tcache-sized chunk (p3=%p)\n", p3); |
18 |
| - printf("will trigger the malloc_consolidate and merge\n"); |
19 |
| - printf("the fastbin chunks into the top chunk, thus\n"); |
20 |
| - printf("p1 and p3 are now pointing to the same chunk !\n\n"); |
| 44 | + printf("\nmalloc_consolidate will merge the fast chunk p1 with top.\n"); |
| 45 | + printf("p3 is allocated from top since there is no bin bigger than it. Thus, p1 = p3.\n"); |
21 | 46 |
|
22 | 47 | assert(p1 == p3);
|
23 | 48 |
|
24 |
| - printf("Triggering the double free vulnerability!\n\n"); |
25 |
| - free(p1); |
| 49 | + printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\n\n"); |
| 50 | + free(p1); // vulnerability |
26 | 51 |
|
| 52 | + printf("So p1 is double freed, and p3 hasn't been freed although it now points to the top, as our\n"); |
| 53 | + printf("chunk got consolidated with it. We have thus achieved UAF!\n"); |
| 54 | + |
| 55 | + printf("We will request a chunk of size 0x400, this will give us a 0x410 chunk from the top\n"); |
| 56 | + printf("p3 and p1 will still be pointing to it.\n"); |
27 | 57 | void *p4 = malloc(0x400);
|
28 | 58 |
|
29 | 59 | assert(p4 == p3);
|
30 | 60 |
|
31 |
| - printf("The double free added the chunk referenced by p1 \n"); |
32 |
| - printf("to the tcache thus the next similar-size malloc will\n"); |
33 |
| - printf("point to p3: p3=%p, p4=%p\n\n",p3, p4); |
34 |
| - |
| 61 | + printf("We now have two pointers (p3 and p4) that haven't been directly freed\n"); |
| 62 | + printf("and both point to the same large-sized chunk. p3=%p p4=%p\n", p3, p4); |
| 63 | + printf("We have achieved duplication!\n\n"); |
35 | 64 | return 0;
|
36 | 65 | }
|
0 commit comments