ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

House of Roman

House of Roman House of Roman无泄漏堆利用的艺术House of Roman是一种极其巧妙的堆利用Heap Exploitation技术。用一句话来概括它是一种在“没有任何内存泄漏Info Leak”的情况下通过“部分覆盖Partial Overwrite”和一点点运气爆破最终控制程序执行流的攻击方法。一、 核心障碍与破局思路1. 核心障碍ASLR地址随机化在现代操作系统中有一个安全机制叫 ASLR。它会在每次程序运行时把堆Heap、栈Stack和系统库Libc加载到内存中完全随机的位置。常规的黑客做法是先找一个漏洞把内存中某个真实的地址“泄漏”出来算出这一次运行时的“基址”再去精确定位目标。而 House of Roman 的魔法在于就算一点地址都泄漏不出来它也能打通。2. 核心武器部分覆盖Partial OverwriteASLR 并不是把地址的每一位都随机化了。因为内存是按“页”Page通常是 0x1000 字节对齐的所以任何地址的最后 12 位也就是十六进制的最后 3 个字符永远是固定不变的。部分覆盖的原理就是在我们不知道完整地址前半部分的情况下只要目标位置和当前位置在同一个区域比如都在 Libc 里或者都在堆里我们只需要覆盖这个指针的最后 1 到 2 个字节。这样既保留了随机化产生的前缀又把指针精确地“拨”到了想要的偏移位置。二、 攻击的终极目标劫持__malloc_hook在 glibc 中有一个叫__malloc_hook的钩子函数指针。每当程序调用malloc分配内存时都会先检查这个指针。我们可以用“快递拦截”来比喻它正常情况下你寄快递调用malloc快递员直接走标准流程分配内存。但系统为了方便程序员调试内存泄漏等 Bug设置了一个“安检专员”__malloc_hook。如果这个专员在场指针不为空所有的包裹必须先交给他处理——程序会直接跳过去执行钩子里存着的地址代码。程序员眼里的“调试工具”在黑客眼里就是官方合法的“执行流劫持后门”。House of Roman 的目标就是把__malloc_hook里的值神不知鬼不觉地改成one_gadgetLibc 中可以直接获取 Shell 的神奇代码片段然后在下一次分配内存时直接拿下系统权限。三、 关键底层机制补充在实战开始前必须了解 glibc 中两种不同的“垃圾回收站”Bin的排队机制Fastbin拿不到 Libc 地址的阵营像“叠盘子”一样是单向链表。释放的块里存的都是堆内存的地址或者 0。这里绝对找不到 Libc 的地址。Unsorted Bin必定留下 Libc 地址的阵营像“手拉手围成一圈”的双向循环链表。这个圈的起始点头部是位于 Libc 内部的全局变量main_arena系统大总管。只要你释放一个较大的块进入 Unsorted Bin为了保持圆圈的完整这个块就必须伸出fd右手和bk左手去拉住main_arena。结论只要堆块进入了 Unsorted Bin它的肚子里就一定会留下真实的 Libc 地址。四、 实战步骤拆解结合内存布局假设我们有一个存在UAF (Use-After-Free释放后重用)漏洞的程序。我们可以修改已经被释放的堆块内部的数据。// 假设程序里有这样的功能 char *chunk_A malloc(0x60); // 大小属于 Fastbin 范围 char *chunk_B malloc(0x80); // 大小属于 Unsorted bin 范围 free(chunk_A); free(chunk_B); // 此时 A 进入 FastbinB 进入 Unsorted bin但由于 UAF我们仍能修改它们第一步通过 Fastbin 伪造目标地址堆上的两级部分覆盖我们需要在__malloc_hook附近伪造一个 Fastbin 堆块。但由于 Fastbin 里只有堆地址而__malloc_hook在 Libc 里我们如何跨越区域这里需要一个极其漂亮的“两级跳”第一跳用堆找堆利用 UAF 修改chunk_A在 Fastbin 中的fd指针的最后 1 个字节使其精确指向处于 Unsorted Bin 中的chunk_B。此时系统被骗误以为chunk_B也是 Fastbin 的一部分。第二跳用 Libc 找 Libcchunk_B的fd里原本装的是main_arena的 Libc 地址。我们再次利用部分覆盖修改chunk_B肚子里这个 Libc 地址的最后 2 个字节。因为main_arena和__malloc_hook都在 Libc 里前缀相同我们成功将其拨到了__malloc_hook附近。错位伪造的魔法为什么是指向“附近”glibc 对 Fastbin 有严格的安全检查检查size字段。黑客发现在__malloc_hook地址往前数 0x23 个字节的地方内存数据恰好存在序列0x7f。如果把指针精确指向__malloc_hook - 0x23系统会把这个0x7f误认为是一个合法的size0x70大小。骗过系统后我们申请出这块内存就能直接覆盖其后方的__malloc_hook。# 代码演示修改 chunk_A 的 fd 指向 __malloc_hook 附近伪造的 chunk edit(chunk_A, p16(0xED))第二步Unsorted Bin Attack借刀杀人写 Libc 地址现在我们控制了__malloc_hook的周边但里面是空的。我们需要往里面写入一个庞大的 Libc 真实地址。这要用到chunk_B。当程序执行malloc把chunk_B从 Unsorted Bin双向链表中摘除时系统为了把断开的链表重新接上会执行解链操作Chunk_B-bk-fd Chunk_B-fd;。黑客的篡改黑客悄悄把chunk_B的左手bk指针硬生生掰开塞到了__malloc_hook - 0x10的位置。# 修改 bk 指向 __malloc_hook - 0x10 edit(chunk_B, 伪造的fd不变, 修改bk指向__malloc_hook-0x10) malloc(0x80) # 触发解链为什么是减去 0x10严密的数学逻辑在 C 语言结构体中堆块的fd指针永远在基地址0x10的偏移处。系统在执行Chunk_B-bk-fd时实际的物理动作是往(Chunk_B-bk) 0x10的地址里写数据。黑客给出提前量-0x10系统自作聪明地加上0x10走过去时一加一减完美抵消。最终系统会亲自把chunk_B右手里握着的大总管地址main_arena分毫不差地写入了__malloc_hook里面整个过程没有使用任何输出函数泄露地址。第三步把 Libc 地址变成 One GadgetLibc 上的部分覆盖此时__malloc_hook里面已经有了一个合法的 Libc 地址main_arena88。main_arena和直接弹 Shell 的one_gadget只有最后几个字节不一样。我们通过第一步布局好的 Fastbin把__malloc_hook申请出来并再次使用部分覆盖只修改最后 2 个字节。# 假设 one_gadget 相对基址的偏移尾数是 0xA456 edit(malloc_hook_chunk, p16(0xA456))第四步命运的 1/16 爆破触发内存按页对齐地址最后的 12 位3 个十六进制字符如456是绝对固定的。但倒数第 4 个字符如上面的A占 4 个 bit是受 ASLR 影响的每次运行都在 0-F 的 16 种可能中随机变化。这意味着我们写入的0xA456中456绝对正确但那个A是我们蒙的。# 最后一步随便 malloc 一个东西触发 __malloc_hook try: malloc(0x10) # 如果蒙对了__malloc_hook 完美指向 one_gadget拿到 Shell # 如果蒙错了程序崩溃。在 CTF 或本地提权中重新运行脚本再试即可。 except: pass总结布局 Fastbin造伪指针利用漏洞修改一个已经被释放的 Fastbin 堆块的fd指针部分覆盖让它指向__malloc_hook附近为你后续能申请到这块内存铺路。Unsorted Bin 盲写借刀杀人修改一个已经被释放的 Unsorted Bin 堆块的bk指针。然后触发一次内存分配申请走这个 bin利用系统底层的解链机制直接把main_arenaLibc 地址盲写到__malloc_hook里面。篡改 Hook偷天换日顺着第 1 步铺好的路把__malloc_hook所在的内存申请出来。此时里面已经有了第 2 步写进去的 Libc 地址。你再次利用部分覆盖把这个地址的最后 2 个字节改成one_gadget的末尾字节完成 1/16 概率的绝杀。例子为了让你能直观地感受到 House of Roman 的精妙之处我们来构建一个极简的漏洞场景并用Python 攻击脚本伪代码结合内存布局一步一步演示它是如何完成这套“盲打”连招的。1. 假设的漏洞场景 (C语言伪代码)假设我们有一个典型的菜单题CTF 中常见的形式程序存在UAF (Use-After-Free)漏洞释放了内存但指针没清空我们还能继续往里面写数据。// 假设程序里有这样的功能 char *chunk_A malloc(0x60); // 大小属于 fastbin 范围 char *chunk_B malloc(0x80); // 大小属于 unsorted bin 范围 char *chunk_C malloc(0x60); free(chunk_A); free(chunk_B); // 此时 A 进入 fastbinB 进入 unsorted bin。 // 漏洞即使 free 了我们依然可以使用 edit 功能修改 A 和 B 里面的数据 (UAF)2. 攻击过程拆解 (结合内存布局)在 ASLR地址随机化开启的情况下我们不知道堆和 Libc 的真实基址。我们只知道它们在内存中的十六进制地址大概长这样堆地址 (Heap):0x55xx_xxxx_xYYY(YYY 是相对固定的偏移)Libc 地址:0x7fxx_xxxx_xZZZ(ZZZ 是相对固定的偏移)接下来开始我们的“部分覆盖”表演第一步修改 Fastbin 的fd指针 (堆基址 1 字节覆盖)目前chunk_A在 fastbin 里它的fd指针指向 0因为它是链表最后一个。我们知道__malloc_hook附近可以伪造一个 chunk。我们要让chunk_A的fd指向那里。# 假设 __malloc_hook 伪造 chunk 的相对偏移最后两个字节是 0xED # 我们利用 UAF只覆盖 chunk_A 的 fd 指针的最低 1 个或 2 个字节 edit(chunk_A, p16(0xED))内存视角修改前chunk_A - fd 0x00000000修改后chunk_A - fd 0x55xx_xxxx_xED(成功利用堆块相对偏移指向了__malloc_hook伪造的 chunk)此时如果我们再分配两次0x60的大小系统就会把__malloc_hook所在的内存当成堆块分配给我们。第二步Unsorted Bin Attack (将 Libc 地址写入 hook)我们要往__malloc_hook里写点东西。现在轮到chunk_B(在 unsorted bin 中) 发挥作用了。Unsorted bin 是一个双向链表被释放的块的fd和bk都指向 Libc 内部的main_arena。# 我们利用 UAF修改 chunk_B 的 bk 指针部分覆盖让它指向 __malloc_hook 的地址。 edit(chunk_B, 伪造的fd不变, 修改bk指向__malloc_hook-0x10) # 接着我们触发一次 malloc把 chunk_B 分配走 malloc(0x80)发生的事情当 ptmalloc 把chunk_B从 unsorted bin 中卸下时会执行类似bk-fd main_arena的操作。因为我们将bk伪造成了__malloc_hook - 0x10所以这个操作等同于把main_arena88的真实 Libc 地址写入到了__malloc_hook里面此时__malloc_hook里面已经有了一个合法的 Libc 地址例如0x7fxx_xxxx_x123。第三步将 main_arena 地址改为 One Gadget (Libc 2字节覆盖)main_arena和直接弹 Shell 的one_gadget都存在于 Libc 模块中它们只有最后几个字节不一样。现在我们通过第一步布局好的 Fastbin把__malloc_hook申请出来并修改里面的值。# 假设 one_gadget 相对 libc 基址的偏移尾数是 0xA456 # 我们向 __malloc_hook 写入时只覆盖最后 2 个字节 edit(malloc_hook_chunk, p16(0xA456))内存视角修改前__malloc_hook内容0x7fxx_xxY1_2123(main_arena88 的地址)修改后__malloc_hook内容0x7fxx_xxYA_4456(变成了 one_gadget 的地址)第四步命运的 1/16 爆破触发仔细看第三步的地址这里就是 1/16 爆破的由来。因为内存按页0x1000对齐只有最后 12 位即 3 个十六进制字符如上面的456是完全固定不变的。倒数第 4 个字符上面的A和1是受 ASLR 影响每次运行都在变化的取值范围是0-F共 16 种可能。所以我们强行写入0xA456时456绝对是正确的。那个A是我们蒙的假设本次运行它恰好被随机成了 A。# 最后一步随便 malloc 一个东西触发 __malloc_hook try: malloc(0x10) # 如果前面蒙对了 A__malloc_hook 完美指向 one_gadget拿到 Shell # 如果蒙错了比如其实本次运行是 C程序崩溃。 except: pass # 重新运行脚本再抽一次卡总结整个过程Fastbin 局部写拿到__malloc_hook附近内存的控制权。Unsorted Bin 攻击借助 glibc 自己内部的链表解链机制往__malloc_hook填入一个 Libc 地址。Libc 局部写把这个 Libc 地址的尾巴“拨”到one_gadget。拼人品触发1/16 概率拿到 Shell。没有任何一个地方使用printf或puts去泄漏真实地址完全是在黑盒状态下通过精准的相对偏移覆盖完成了内存结构的劫持。这也是 glibc ptmalloc 利用中最有艺术感的打法之一。
返回列表