高版本下tcache bin如何通过key值绕过double free
字数 1210 2025-08-22 18:37:22

Tcache Bin Double Free绕过技术分析

1. Tcache Entry结构分析

在libc 2.35中,进入tcache bin的chunk使用以下结构体:

typedef struct tcache_entry {
    struct tcache_entry *next;  // 指向下一个tcache entry的指针
    uintptr_t key;              // 用于检测double free的key值
} tcache_entry;

2. Tcache Put机制

当chunk被放入tcache bin时,会执行以下操作:

tcache_put(mchunkptr chunk, size_t tc_idx) {
    tcache_entry *e = (tcache_entry *)chunk2mem(chunk);
    
    // 标记这个chunk为"在tcache中",用于检测double free
    e->key = tcache_key;
    
    // 加密next指针
    e->next = PROTECT_PTR(&e->next, tcache->entries[tc_idx]);
    
    tcache->entries[tc_idx] = e;
    ++(tcache->counts[tc_idx]);
}

其中PROTECT_PTR宏定义为:

#define PROTECT_PTR(pos, ptr) \
    ((__typeof(ptr)) ((((size_t) pos) >> 12) ^ ((size_t) ptr)))

3. 堆地址泄露原理

当释放一个tcache chunk时:

  • 如果它是对应tcache bin的第一个chunk,其next值为:(chunk_mem_addr >> 12) ^ 0
  • 由于地址按页(0x1000)对齐,右移12位后可以左移恢复大部分地址信息

4. Tcache Get机制

从tcache bin中取出chunk时:

tcache_entry *e = tcache->entries[tc_idx];
tcache->entries[tc_idx] = REVEAL_PTR(e->next);
--(tcache->counts[tc_idx]);
e->key = 0;
return (void *)e;

REVEAL_PTR宏定义为:

#define REVEAL_PTR(ptr) PROTECT_PTR(&ptr, ptr)

5. Double Free检测机制

_int_free函数中,有以下检测逻辑:

if (__glibc_unlikely(e->key == tcache_key)) {
    tcache_entry *tmp;
    size_t cnt = 0;
    
    for (tmp = tcache->entries[tc_idx]; tmp; tmp = REVEAL_PTR(tmp->next), ++cnt) {
        if (cnt >= mp_.tcache_count)
            malloc_printerr("free(): too many chunks detected in tcache");
        if (__glibc_unlikely(!aligned_OK(tmp)))
            malloc_printerr("free(): unaligned chunk detected in tcache 2");
        if (tmp == e)
            malloc_printerr("free(): double free detected in tcache 2");
    }
}

6. Double Free绕过技术

6.1 常规绕过方法

  1. 多指针记录法

    • 可以记录多个chunk指针且free后可以修改
    • 构造A->B,然后修改A的next值为C,形成A->C
  2. tcache填满法

    • 填满tcache bin使其进入fastbin
    • 构造A->B->A的fastbin链
    • 然后可以按需操作

6.2 单指针情况下的绕过

当只能记录一个chunk指针时,可以通过以下方法绕过:

  1. 关键思路:修改已释放chunk的key值
  2. 具体步骤
    • 第一次free(chunkA) - key被设置为tcache_key
    • 修改chunkA的key值为其他值(如0)
    • 再次free(chunkA) - 由于key != tcache_key,不会触发double free检测
    • 此时chunkA被再次放入tcache bin

6.3 绕过原理分析

检测逻辑if (e->key == tcache_key)是关键:

  • 第一次free后,key被设为tcache_key
  • 如果修改key值,第二次free时检测条件不成立
  • 因此不会进入遍历检查的循环,也就不会发现double free

7. 防御措施分析

当前tcache的防御机制:

  1. key值检测 - 可被修改绕过
  2. 链表遍历检测 - 依赖key值检测
  3. 指针加密 - 增加利用难度但不防止double free

8. 利用场景

这种绕过技术在以下场景特别有用:

  • 只能控制有限数量指针的情况
  • 存在UAF漏洞可以修改已释放chunk的内容
  • tcache bin未填满无法进入fastbin的情况

9. 总结

高版本glibc中tcache的double free防护主要依赖key值检查,通过修改已释放chunk的key值可以有效地绕过这一防护机制。理解这一技术需要对tcache的内部结构和工作原理有深入认识,特别是key值的作用和PROTECT_PTR/REVEAL_PTR的指针加密机制。

Tcache Bin Double Free绕过技术分析 1. Tcache Entry结构分析 在libc 2.35中,进入tcache bin的chunk使用以下结构体: 2. Tcache Put机制 当chunk被放入tcache bin时,会执行以下操作: 其中 PROTECT_PTR 宏定义为: 3. 堆地址泄露原理 当释放一个tcache chunk时: 如果它是对应tcache bin的第一个chunk,其next值为: (chunk_mem_addr >> 12) ^ 0 由于地址按页(0x1000)对齐,右移12位后可以左移恢复大部分地址信息 4. Tcache Get机制 从tcache bin中取出chunk时: REVEAL_PTR 宏定义为: 5. Double Free检测机制 在 _int_free 函数中,有以下检测逻辑: 6. Double Free绕过技术 6.1 常规绕过方法 多指针记录法 : 可以记录多个chunk指针且free后可以修改 构造A->B,然后修改A的next值为C,形成A->C tcache填满法 : 填满tcache bin使其进入fastbin 构造A->B->A的fastbin链 然后可以按需操作 6.2 单指针情况下的绕过 当只能记录一个chunk指针时,可以通过以下方法绕过: 关键思路 :修改已释放chunk的key值 具体步骤 : 第一次free(chunkA) - key被设置为tcache_ key 修改chunkA的key值为其他值(如0) 再次free(chunkA) - 由于key != tcache_ key,不会触发double free检测 此时chunkA被再次放入tcache bin 6.3 绕过原理分析 检测逻辑 if (e->key == tcache_key) 是关键: 第一次free后,key被设为tcache_ key 如果修改key值,第二次free时检测条件不成立 因此不会进入遍历检查的循环,也就不会发现double free 7. 防御措施分析 当前tcache的防御机制: key值检测 - 可被修改绕过 链表遍历检测 - 依赖key值检测 指针加密 - 增加利用难度但不防止double free 8. 利用场景 这种绕过技术在以下场景特别有用: 只能控制有限数量指针的情况 存在UAF漏洞可以修改已释放chunk的内容 tcache bin未填满无法进入fastbin的情况 9. 总结 高版本glibc中tcache的double free防护主要依赖key值检查,通过修改已释放chunk的key值可以有效地绕过这一防护机制。理解这一技术需要对tcache的内部结构和工作原理有深入认识,特别是key值的作用和PROTECT_ PTR/REVEAL_ PTR的指针加密机制。