Skip to content

House of Botcake

复制本地路径 | 在线编辑

原理快速说明

前提:可以进行 Double free,下面的例子是可以 free 两次 chunk9
结果:任意地址控制

  1. 连续 malloc 9 块大于 fastbin chunk 大小的堆块,然后释放 7 块填充 tcachebin

  2. 然后 free chunk8 和 chunk9(第一次 free),会落入 unsortedbin 并进行合并

  3. 这时候我们将一块 0x100 大小的 chunk 申请出来,然后再 free 掉 chunk9(第二次 free)

  4. 此时我们完成了 Double Free,例如我们可以申请 unsortedbin 中的大块,这样可以修改 chunk9 里面的内容;比如修改掉 next 指针,最后申请两次 tcache 大小的块,就可以得到目标地址。

PS: 第四步的举例是修改 next 指针,但由于 safe-linking 的存在(即存储的不是 next,而是 next 和块地址异或的值),所以我们需要知道 heap 地址才行。本质上第四步是最终可以修改 freed chunk 的内容,即拥有了 tcache poisoning 的能力,至于最终攻击,还需要其他辅助。

Double Free --> house of botcake --> 拥有 tcache poisoning 能力

具体细节

上面的还是有点抽象,下面具体用示例来讲,肯定不可能一下子看懂,慢慢来就明白了。

第一步,申请 9 个 chunk,其中原理中的 chunk8 这里叫做 prev,chunk9 叫做 a。同时我们最后还要申请一个小块,避免和 top chunk 合并。

for(int i=0; i<sizeof(x)/sizeof(intptr_t*); i++){
        x[i] = malloc(0x100);
}

intptr_t *prev = malloc(0x100);
intptr_t *a = malloc(0x100);
malloc(0x10);

第二步,依次 free,这样前面 7 个 chunk 掉入 tcache,剩下两个在 unsorted 中。可以看到 preva 合并了,并且合并的时候不会清洗内容,所以freea 中原先的 fd & bk 指针依然存在。

for(int i=0; i<7; i++){
        free(x[i]);
}
free(a);
free(prev);

第三步,申请一个块,目的是从 tacache 中留一个空间;然后我们 free(a),此时 a 这个地址即在 tcache 中也在 unsorted 中。

其中 free(a) 就是假设我们可以进行 Double Free。回顾本攻击的意义,我们可以 Double Free,但由于 tcache key 的存在,我们会被检测出来。而本攻击的目的就在于规避这些检查,让 Double Free 最终成功。

malloc(0x100);  // take one out from tcache
/*VULNERABILITY*/
free(a);// a is already freed
/*VULNERABILITY*/

为什么这里 free(a) 不会受到检查,因为 a 中的 bk 指针帮我们规避了 tcache key 的检查:

    if (__glibc_unlikely (e->key == tcache_key))

这一步之后的空间如下,可以看到 afd 指针被修改成指向 tcache 中下一个 chunk;而 bk 指针对应的是 e->key,被修改成了 tcache key:

现在缓一缓,仔细体会如下两点:

  1. 本质上是修改 ae->key 位置内容,而这个位置对应的正好是 bk 指针。所以我们要让 a 落入了双向链表的堆。由于 tcache 满了的第一站就是 unsorted,所以理所当然用 unsorted 就可以。
  2. 那让 a 落入到 unsorted 之后,它的 bk 已经被改掉,我们直接 free(a) 为什么不行?因为 free(a) 时判断 a 是否已经被 free有一个判断是根据下一个 chunk 的 PREV_INUSE 位。所以 a 被检测已经 free 了,而利用 unsorted 合并,就规避了这一点。

第四步,此时我们把 Unsorted 上面的大块申请了,然后可以该里面的内容,把 a->next 指针改掉;然后两次 malloc,第一次是把 a 从 tcache 中拿下来,第二次就是把 a->next 从 tcache 中拿到手。

intptr_t *unsorted = malloc(0x100 + 0x100 + 0x10);
// 为了避免 safe unlinking,Demo 直接就有变量 a 来表示地址,实际操作中我们需要泄露 heap 地址
unsorted[0x110/sizeof(intptr_t)] = ((long)a >> 12) ^ (long)target_addr;

a = malloc(0x100);

intptr_t *target_content = malloc(0x100);

参考:

  1. https://blog.csdn.net/Mr_Fmnwon/article/details/142468380
  2. https://4xura.com/binex/house-of-botcake/

Comments