Tcache Stashing Unlink Attack
字数 1702 2025-08-22 12:22:24

Tcache Stashing Unlink Attack 技术分析

1. 攻击概述

Tcache Stashing Unlink Attack 是一种针对 glibc 堆管理器的攻击技术,即使在较新版本的 glibc(如 2.36)中仍然有效。该攻击利用了 small bin 向 tcache 转移 chunk 时的逻辑缺陷,可以实现任意地址写入 libc 地址(main_arena)和伪造 chunk 分配。

2. 核心原理

2.1 漏洞代码分析

漏洞存在于 malloc.c 中处理 small bin 分配的逻辑:

if (in_smallbin_range(nb)) {
    idx = smallbin_index(nb);
    bin = bin_at(av, idx);
    if ((victim = last(bin)) != bin) {
        bck = victim->bk;
        if (__glibc_unlikely(bck->fd != victim))
            malloc_printerr("malloc(): smallbin double linked list corrupted");
        set_inuse_bit_at_offset(victim, nb);
        bin->bk = bck;
        bck->fd = bin;
        
        // 将剩余 small bin chunks 放入 tcache
        size_t tc_idx = csize2tidx(nb);
        if (tcache && tc_idx < mp_.tcache_bins) {
            mchunkptr tc_victim;
            while (tcache->counts[tc_idx] < mp_.tcache_count 
                   && (tc_victim = last(bin)) != bin) {
                if (tc_victim != 0) {
                    bck = tc_victim->bk;  // 关键点:没有检查 bck->fd == tc_victim
                    set_inuse_bit_at_offset(tc_victim, nb);
                    bin->bk = bck;
                    bck->fd = bin;  // 任意地址写入 bin 地址
                    tcache_put(tc_victim, tc_idx);
                }
            }
        }
        void *p = chunk2mem(victim);
        alloc_perturb(p, bytes);
        return p;
    }
}

2.2 关键漏洞点

  1. 缺乏完整性检查:当将 small bin 中的 chunk 转移到 tcache 时,没有检查 tc_victim->bk->fd == tc_victim,而 small bin 分配时有此检查
  2. 任意地址写入:通过控制 tc_victim->bk,可以在 bck->fd 处写入 bin 的地址(main_arena+96)
  3. calloc 绕过 tcache:calloc 会直接使用 small bin 而不会先检查 tcache,这是攻击能够触发的关键

3. 攻击条件

  1. 能够覆盖 victim chunk 的 bk 指针
  2. 至少能够使用一次 calloc 分配内存
  3. 需要一个可写地址来绕过 glibc 的检查(fake_chunk->bk 必须指向可写地址)

4. 攻击步骤详解

4.1 堆布局准备

  1. 分配 9 个 0x90 大小的 chunk
  2. 释放其中 7 个到 tcache bin:
    • 不能连续释放相邻 chunk,防止 unsorted bin 合并
    • 先释放 chunk3-chunk8
    • 然后释放 chunk1(填满 tcache)
  3. 释放 chunk0 和 chunk2 到 unsorted bin
  4. 分配一个大于 0x90 的 chunk,将 chunk0 和 chunk2 放入 small bin
  5. 分配两个 0x90 chunk,从 tcache 取出,为 small bin 转移到 tcache 腾出空间

4.2 伪造 chunk

  1. 在目标地址(如栈上)构造 fake chunk
  2. 设置 fake_chunk->bk 为可写地址(如 fake_chunk+0x10)以绕过检查
  3. 修改 small bin 中最后一个 chunk 的 bk 指针指向 fake_chunk-0x10

4.3 触发攻击

  1. 使用 calloc 分配 0x90 chunk:
    • 从 small bin 取出一个 chunk
    • 将剩余 small bin chunk(被我们控制 bk 的)和 fake chunk 放入 tcache
    • 在 fake_chunk->bk->fd(即 fake_chunk+0x10+0x10)处写入 bin 地址
  2. 后续 malloc(0x90) 将返回 fake_chunk+0x10

5. 保护机制绕过

关键检查点:

if (__glibc_unlikely(bck->fd != victim)) {
    malloc_printerr("malloc(): smallbin double linked list corrupted");
}

绕过方法:

  • 设置 fake_chunk->bk 指向一个可写地址(如 fake_chunk+0x10)
  • 确保该地址+0x10(即 bck->fd)处可写,这样检查时不会崩溃

6. 攻击效果

  1. 任意地址写入:在 fake_chunk->bk->fd 处写入 main_arena 地址
  2. 任意地址分配:后续分配可以从伪造的 chunk 处返回内存

7. 攻击模板

# 假设 target 是我们要写入的地址
# 绕过保护:target+0x8 处必须是一个可读写地址

# 1. 堆布局
for i in range(9):
    add(0x90)  # 0,1,2,3...

for i in range(3, 9):
    free(i)
free(1)
free(0)
free(2)

add(0xa0)     # 将 unsorted bin 转为 small bin
add(0x90)     # 腾出 tcache 空间
add(0x90)

# 2. 修改 bk 指针
edit(2, bk=target-0x10)  # 修改 small bin 最后一个 chunk 的 bk

# 3. 触发攻击
calloc(1, 0x90)  # 触发 small bin 向 tcache 转移

# 4. 获取伪造 chunk
malloc(0x90)  # 将返回伪造的 chunk

8. 实际应用场景

  1. 劫持程序控制流:通过写入 libc 地址或分配特定位置 chunk 实现
  2. 绕过 ASLR:泄漏 libc 地址
  3. 实现任意地址读写:通过伪造 chunk 实现内存读写

9. 防御措施

  1. 更新 glibc:虽然该攻击在较新版本仍有效,但应使用最新版本
  2. 加强堆指针检查:确保所有 chunk 指针都经过严格验证
  3. 限制 calloc 使用:避免在关键代码路径使用 calloc

该攻击技术展示了 glibc 堆管理器中安全检查的不一致性,强调了即使现代防护机制下,细微的逻辑缺陷仍可能导致严重的安全问题。

Tcache Stashing Unlink Attack 技术分析 1. 攻击概述 Tcache Stashing Unlink Attack 是一种针对 glibc 堆管理器的攻击技术,即使在较新版本的 glibc(如 2.36)中仍然有效。该攻击利用了 small bin 向 tcache 转移 chunk 时的逻辑缺陷,可以实现任意地址写入 libc 地址(main_ arena)和伪造 chunk 分配。 2. 核心原理 2.1 漏洞代码分析 漏洞存在于 malloc.c 中处理 small bin 分配的逻辑: 2.2 关键漏洞点 缺乏完整性检查 :当将 small bin 中的 chunk 转移到 tcache 时,没有检查 tc_victim->bk->fd == tc_victim ,而 small bin 分配时有此检查 任意地址写入 :通过控制 tc_victim->bk ,可以在 bck->fd 处写入 bin 的地址(main_ arena+96) calloc 绕过 tcache :calloc 会直接使用 small bin 而不会先检查 tcache,这是攻击能够触发的关键 3. 攻击条件 能够覆盖 victim chunk 的 bk 指针 至少能够使用一次 calloc 分配内存 需要一个可写地址来绕过 glibc 的检查(fake_ chunk->bk 必须指向可写地址) 4. 攻击步骤详解 4.1 堆布局准备 分配 9 个 0x90 大小的 chunk 释放其中 7 个到 tcache bin: 不能连续释放相邻 chunk,防止 unsorted bin 合并 先释放 chunk3-chunk8 然后释放 chunk1(填满 tcache) 释放 chunk0 和 chunk2 到 unsorted bin 分配一个大于 0x90 的 chunk,将 chunk0 和 chunk2 放入 small bin 分配两个 0x90 chunk,从 tcache 取出,为 small bin 转移到 tcache 腾出空间 4.2 伪造 chunk 在目标地址(如栈上)构造 fake chunk 设置 fake_ chunk->bk 为可写地址(如 fake_ chunk+0x10)以绕过检查 修改 small bin 中最后一个 chunk 的 bk 指针指向 fake_ chunk-0x10 4.3 触发攻击 使用 calloc 分配 0x90 chunk: 从 small bin 取出一个 chunk 将剩余 small bin chunk(被我们控制 bk 的)和 fake chunk 放入 tcache 在 fake_ chunk->bk->fd(即 fake_ chunk+0x10+0x10)处写入 bin 地址 后续 malloc(0x90) 将返回 fake_ chunk+0x10 5. 保护机制绕过 关键检查点: 绕过方法: 设置 fake_ chunk->bk 指向一个可写地址(如 fake_ chunk+0x10) 确保该地址+0x10(即 bck->fd)处可写,这样检查时不会崩溃 6. 攻击效果 任意地址写入:在 fake_ chunk->bk->fd 处写入 main_ arena 地址 任意地址分配:后续分配可以从伪造的 chunk 处返回内存 7. 攻击模板 8. 实际应用场景 劫持程序控制流:通过写入 libc 地址或分配特定位置 chunk 实现 绕过 ASLR:泄漏 libc 地址 实现任意地址读写:通过伪造 chunk 实现内存读写 9. 防御措施 更新 glibc:虽然该攻击在较新版本仍有效,但应使用最新版本 加强堆指针检查:确保所有 chunk 指针都经过严格验证 限制 calloc 使用:避免在关键代码路径使用 calloc 该攻击技术展示了 glibc 堆管理器中安全检查的不一致性,强调了即使现代防护机制下,细微的逻辑缺陷仍可能导致严重的安全问题。