Tcache_Attack(1)

本文最后更新于 2024年11月10日 下午

Tcache_Attack(1)

Tcache_konwledge(引用CTF-Wiki)

tcache是glibc 2.26(Ubuntu 17.10)之后引入的一种技术,其目的是为了提升堆管理的性能。我们都知道,一旦某个整体的应用添加了更加复杂的执行流程,那么就意味着整体执行的速度就会降低,那么为了弥补这一部分的欠缺,就不得不有所牺牲。所以虽然提升了整体的性能,但却舍弃了很多安全检查,这就意味着更多新的漏洞就伴随而来,也增添了很多利用方式

tcache引入了两个新的结构体:tcache_entrytcache_perthread_struct。增添的两个结构体其实与fastbin有些类似,但是也有一定的区别

tcache_entry结构体如下:

1
2
3
4
5
typedef struct tcache_entry
{
struct tcache_entry *next;
} tcache_entry;

与fastbin不同的是,tcache_entry的next指向的是下一个chunk的data域,fastbin的next指向的是下个chunk的头指针。

tcache_perthread_struct结构体如下:

1
2
3
4
5
6
7
8
9
10
typedef struct tcache_perthread_struct
{
char counts[TCACHE_MAX_BINS];
tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;

# define TCACHE_MAX_BINS 64

static __thread tcache_perthread_struct *tcache = NULL;

这个结构体是用来管理tcache链表的,这个结构体位于heap段的起始位置,其中

  • tcache_entry 用单向链表的方式链接了相同大小的处于空闲状态(free 后)的 chunk
  • counts 记录了 tcache_entry 链上空闲 chunk 的数目,每条链上最多可以有 7 个 chunk

tcache_get()函数

1
2
3
4
5
6
7
8
9
10
static __always_inline void *
tcache_get (size_t tc_idx)
{
tcache_entry *e = tcache->entries[tc_idx];
assert (tc_idx < TCACHE_MAX_BINS);
assert (tcache->entries[tc_idx] > 0);
tcache->entries[tc_idx] = e->next;
--(tcache->counts[tc_idx]);
return (void *) e;
}

可以看到这个函数很简单,获取一个chunk指针,然后count-1,没有太多的安全检查

tcache_put()函数

1
2
3
4
5
6
7
8
9
10
static __always_inline void
tcache_put (mchunkptr chunk, size_t tc_idx)
{
tcache_entry *e = (tcache_entry *) chunk2mem (chunk);
assert (tc_idx < TCACHE_MAX_BINS);
e->next = tcache->entries[tc_idx];
tcache->entries[tc_idx] = e;
++(tcache->counts[tc_idx]);
}

tcache_put()函数就是简单在tcahe链头插入一个chunk,也没有做过多的安全检查

Tcache usage

  • 第一次malloc时会在heap头回显一块内存用来存放tcache_perthread_struct,这块内存size一般为0x251

  • 申请的内存块符合 fastbin 大小时并且在 fastbin 内找到可用的空闲块时,会把该 fastbin 链上的其他内存块放入 tcache 中。

  • 申请的内存块符合 smallbin 大小时并且在 smallbin 内找到可用的空闲块时,会把该 smallbin 链上的其他内存块放入 tcache 中。

  • 当在 unsorted bin 链上循环处理时,当找到大小合适的链时,并不直接返回,而是先放到 tcache 中,继续处理。

  • tcache 取出:在内存申请的开始部分,首先会判断申请大小块,在 tcache 是否存在,如果存在就直接从 tcache 中摘取,否则再使用_int_malloc 分配。

PWN Tcache

tcache poisoning

因为tcache_get没有对next进行检查,所以我们可以覆盖tcache的fd指针,从而实现对任意地址的malloc

在这里插入图片描述

我们申请两个chunk并释放

在这里插入图片描述

可以看到这时chunkb的fd是chunka

在这里插入图片描述

而经过覆盖之后就可以把任意地址挂进tcachebin

在这里插入图片描述

tcache dup

这种利用方法其实就是fastbin中的double_free,如果在释放的时候指针未置空,可以对一个chunk释放两次

我们申请一个chunk a连续释放两次

在这里插入图片描述

可以看到此时同一个chunk a被挂进去两次

此时我们在申请chunk b,那么这次启用的是第二次挂进去的chunk a,此时打印chunk_a和chunk_b的fd指针会发现其实都是chunk_a

在这里插入图片描述

tcache house of spirit

tcache house of spirit这种利用方式是由于tcache_put()函数检查不严格造成的,跟fastbin的很像,

他的原理就是,将一个tcache bin中的chunk的fd指针改成我们伪造的fake_chunk,然后申请一个chunk,那么malloc在申请的同时,会把本没有free的fake_chunk挂进tcache bin,甚至他的检查比fastbin还要松一点,这里就不具体说明了,请参考博主的另一篇文章

Fastbin-attack-House-Of-Spirit


Tcache_Attack(1)
http://example.com/2024/10/24/Tcache-Attack/
作者
清风
发布于
2024年10月24日
许可协议