Tcache机制及漏洞利用方法

0x00 写在前面

Tcache机制是在libc-2.26中引入的一个新的堆管理机制。掐指一算,libc-2.26发布距今应该也有一两年了。由于各种不可控因素,到近期才将其提上日程。

0x01 What's New

首先得介绍在libc-2.26中新引进的tcache_perthread_structtcache_entry两个结构体。

  • tcache_perthread_struct
#define TCACHE_MAX_BINS 64
typedef struct tcache_perthread_struct{
    char counts[TCACHE_MAX_BINS];
    tcache_entry *entries[TCACHE_MAX_BINS];
}tcache_perthread_struct;
  • tcache_entry
typedef struct tcache_entry{
    struct tcache_entry *next;
}tcache_entry;

从源码中不难看出,tcache_perthread_structTcache机制中起管理作用,在默认情况下,sizeof(tcache_perthread_struct) == 0x240,即可以对低于0x400大小的堆块进行管理。其中tcache_pthread_struct.counts[i]64bit系统中对应大小为8 * i堆块的Tcachebin的数量,最大可为7tcache_pthread_struct.tcache_entry[i]则指向该大小对应的第一个Tcachebinfd的位置。

  • 举个栗子
    为对这两个结构体有直观的印象,下面的例子中释放了2个大小为0x201个大小为0x20的堆块。
    堆块分布

    其中第一个0x250的堆块是在第一次申请内存时,会分配一个空间用于存放tcache_pthread_struct(0x240 + 堆头 == 0x250)
    tcache_pthread_struct

    上图中则展示了第一个堆块,即tcache_perthread_struct中存放的数据。
    图中红色方框内的数据,即对应结构体中的count,共0x40字节,每字节对应相应大小Tcachebin中的个数。如绿色方框中对应size0x20大小的Tcachebin中有2个空闲堆块,而黄色方框中则对应size0x30大小的Tcachebin中有1个空闲堆块。
    图中蓝色方框内的数据,即对应结构体中的tcache_entry,共0x200字节(0x40 * 8 == 0x200),每一个指针对应相应大小Tcachebin中第一个堆块的入口地址。如绿色箭头对应size0x20大小的Tcachebin的入口地址,二黄色箭头则对应size0x30大小的Tcachebin的入口地址。

0x02 What's Tcachebin

从宏观来看,Tcachebin的各项操作与Fastbin大同小异,如FILO(先进后出)的单循环链表、精确分配(不切割)、free后为防止合并后一个堆块的inuse位不置0等。
但在细节上仍存在些许差异,如Fastbinfd是指向链表中下一个堆块的堆头,而Tcachebinfd则是直接指向链表中下一个堆块的fd。除此之外,在从Tcachebin中申请回内存块时,并没有特定的代码去检验该内存块的大小是否与这条Tcachebin所管理的大小相吻合!
以上两点差异意味着在Tcachebin中利用类似Fastbin Attack的技巧时,不需要再去找到合适的地址伪造size位,不需要再去计算堆头到data区域的偏移,而是指哪儿打哪儿(fd伪造到哪里,之后写的就是哪里)。

  • free
    在该机制中释放大小低于0x400字节的堆块时会首先放入Tcachebin,而不是原来的FastbinUnsortbin。当对应大小的Tcachebin中放满7个空闲堆块后,下一次free的堆块才会放入对应的FastbinUnsortbin中。在放入Tcachebin时会调用tcache_put函数,其代码如下:
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]);
}

总的来说,就是将free的堆块插入Tcachebin的前端,将fd指向前一个堆块,并将对应的tcache_entry指向当前堆块,再将count+1

  • malloc
    相对应与内存释放,内存申请会遇到很多种不同的内存布局情况。在申请大小低于0x400的堆块时,首先会考虑从Tcachebin中去寻找。如上文中提到的,从Tcachebin中取出堆块时的逻辑除了不会检查size位之外,几乎与Fastbin相同,只会进行精准匹配,不会进行切割,在取出时会调用tcache_get函数,其代码入下:
tcache_get(size_t tc_idx)
{
    tcache_entry *e = tcache->entries[tc_idx];
    assert(tc_idx < TCACHE_MAX_BINS);
    assert(tcache->counts[tc_idx] > 0);
    tcache->entries[tc_idx] = e->next;
    --(tcache->counts[tc_idx]);
    return (void*)e;
}

若对应大小的Tcachebin为空,则会从对应大小的bin中去寻找(寻找顺序同之前版本)。在这种情况下,即Tcachebin未满时,却从Fastbin/Smallbin中取出堆块,则会将链上的其他堆块都链入Tcachebin中。其具体算法是首先将Fastbin/Smallbin中取出的堆块指针进行保存,并判断该大小对应的Tcachebin是否未满,若未满则将其之后的堆块按照Fastbin/Smallbin的分配顺序将堆块链入Tcachebin中,直到对应大小的Tcachebin放满或Fastbin/Smallbin的链为空,最后将之前取出的堆块指针返回给用户使用。由于是按照Fastbin/Smallbin的分配顺序将堆块放入Tcachebin中,因此不难判断,最从Tcachebin中申请的堆块顺序是与正常从Fastbin/Smallbin中申请堆块顺序时反向的。

  • 关于将Fastbin/Smallbin中堆块放入Tcachebin中的操作
    针对这个Tcachebin未满,放入堆块的操作,之前在网上看到大部分的描述都是先把大小相同的堆块从Fastbin/Smallbin中放入Tcachebin后再进行分配,但在进行调试的时候发现跟这个描述略有出入,不是很理解,于是通过阅读源码可以非常清楚地看到整个操作的逻辑。
    Fastbin:3594行保存了Fastbin中即将分配的指针,3608-3631行将其后的堆块按Fastbin的申请顺序(FILO)放入Tcachebin3632行将第3594行保存的指针返回给用户使用。
    Smallbin:3652行保存了Smallbin中即将分配的指针,3664-3689行将其后的堆块按Smallbin的申请顺序(FIFO)放入Tcachebin3690行将第3652行保存的指针放回给用户使用。
    下面以Fastbin为例,贴出简化后的代码:
if(nb <= get_max_fast())                                            //申请范围在Fastbin之内
{
   idx = fastbin_index(nb);                                       //找到对应大小的Fastbin索引
   mfastbinptr *fb = &fastbin(av , idx);                 //通过索引找到入口
   victim = *fb;                                                         //注意这里,保存了第一个即将分配堆块的指针
   if(victim != NULL)                                                //如果Fastbin中有堆块
   {
       *fb = victim->fd;                                            //此时fb为即将分配堆块的fd
       ............
#if USE_TCACHE
       ............
       while(tcache->counts[tc_idx] < mp_.tcache_count && (tc_victim = *fb) != NULL)
       {                                                                      //直到Tcachebin放满,或Fastbin取空为止
           *fb = tc_victim->fd;                                  //循环遍历
           ............
           tcache_put(tc_victim , tc_idx);                //将该堆块放入Tcachebin中
       }
#endif
       void *p = chunk2mem(victim);                  //注意这里的victim为最开始保存的即将分配的堆块、
       return p;                                                      //返回给用户使用
   }
}

0x03 How To Pwn

由于这方面的题目接触的不算特别多,目前就只从大佬的博客上了解了一些基础的利用方法,可能以后会遇到再进行更详细和深入的归纳总结吧。

  • Tcache poisoning
    在上一部分中也提到过这种方法。与Fastbin Attack类似,篡改Tcachebin中的fd字段,导致在申请被篡改堆块后的下一个堆块时能够申请到任意地址。与Fastbin相比,Tcachebin中为了得到更高的效率而舍去了安全性,在进行申请时没有对size位进行校验,而且由于Tcachebin中的fd是指向下一个堆块的fd(Fastbinfd是指向下一个堆块的堆头),因此指向的地址即是申请后写数据的地址,不再需要去考虑堆头的偏移。

  • Tcache dup
    这是Tcache机制刚推出的几个版本中,在进行free操作时没有对这个堆块进行一个安全检测而导致可以对同一个堆块进行多次free,那么就会变成一个Tcachebin链上链了两个相同的堆块(我指向我自己),后面也就不用多说了。但值得一提的是在libc-2.29版本中加入了检查机制(源码4201-4216行),会在堆块进行free时检查这个堆块是否已经存在于这条链上,如果存在则会报"free():double free detected in tcache 2"的错误,因此这种直接double free利用方式存在于libc-2.26libc-2.28的版本中。

  • Tcache perthread corruption
    在最开始介绍结构体时提到的tcache_perthread_struct结构体,该结构体size0x250,是管理整个Tcachebin的结构体,如果对这个结构体有写权限,那么可以控制任意大小Tcachebin的入口地址。

  • U2T
    U2TUnsortbin 2 Tcachebin,这种叫法是在一篇文章中看到的,也只看到过一次,主要是配合Off By OneOff By NULL的漏洞,使Unsortbin在合并过程中将中间的Tcachebin合并,从而达到修改fd字段的效果。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容

  • tcache是libc2.26之后引进的一种新机制,之前一直没做到,然后做几道题熟悉一下 原理及机制 简单来说就是...
    2mpossible阅读 4,729评论 5 1
  • emmm这一篇既是开始,也是一个小小的总结。 Q1:为什么是ptmalloc呢? A:内存的分配释放都很频繁,pt...
    BJChangAn阅读 1,568评论 0 1
  • libc2.26 之后的 Tcache 机制 1. Tcache 概述 ​ tcache是libc2.26之...
    Nevv阅读 8,510评论 0 2
  • 昨晚老公跟他朋友喝酒喝到凌晨两点到家,他把我叫醒,看他心情极差。因为儿子最近学业很苦,他打电话给前妻,希望对方来关...
    多多少少7阅读 255评论 0 3
  • 22岁是很多人的人生节点,告别校园生活,步入社会,从懵懂无知的学生妹变成独挡一面的女强人。 在这个年纪,一方面热血...
    不甘现状的毕业喵阅读 752评论 4 5