PWN——uaf漏洞学习
PWN——uaf漏洞
1.uaf漏洞原理
在C语言中,我们通过malloc族函数进行堆块的分配,用free()函数进行堆块的释放。在释放堆块的过程中,如果没有将释放的堆块置空,这时候,就有可能出现use after free的情况。这里我写了一个demo
#include<stdio.h>
#include<stdlib.h>
typedef struct demo
{
char *s;
void(*func)(char *);
}DEMO;
void eval(char command[])
{
system(command);
}
void echo(char content[])
{
printf("%s",content);
}
int main()
{
DEMO*p1;
DEMO*p2;
p1=(DEMO*)malloc(sizeof(struct demo));
p1->s="I will tell you what's uaf.";
printf("%s\n",p1->s);
printf("p1 malloc address:%p\n",p1);
p1->func=echo;
p1->func("use after free!\n"); free(p1);
p1->s="~Heihei~";
printf("%s\n",p1->s);
p2=(DEMO*)malloc(sizeof(struct demo));
p2->func=eval;
// p1->func=eval;
printf("p2 malloc address:%p\n",p2);
p2->func("whoami"); p1=NULL;
printf("Now,I can't use address of p1.");
p2->func("whoami");
return 0;
}
运行的结果如下图所示

可以看出,在free()堆块之后,没有将堆块置空,堆块处于悬空状态,导致被free掉的堆块依然可以被使用。同时,如果我们申请相同大小的堆块的话,由于ptmalloc的堆管理机制,重新分配的堆块的位置和我们释放的堆块地址是一样的。但是,在将p1置空之后,再次使用堆块的时候,就会报出段错误。
其实,一般的uaf漏洞利用,和我们上图的demo也是类似的,都是定义一个结构体,结构体中有一个函数指针,然后free,将chunk添加进fastbin,再次分配相同大小的内存块,这时候分配的就是刚才free掉悬空的堆块,然后改写函数指针,劫持数据流,配合不同的函数指针,可以实现任意地址读,任意地址写,以及命令执行。
注:这里第二次分配内存的时候,我们一般分配相同数据类型。比如上面的例子中,第一个释放的堆块p1的是DEMO类型的结构体,第二次分配的时候,我们们分配的p2也是一个DEMO类型的结构体,这种情况下,p2一定用的是之前p1的内存。但是在有些情况下,如果我们分配的数据类型不同,但是前后分配大小相同,也可能造成堆块的再次利用。
2.例题
这道例题,是pwnable.kr上的uaf这道题目,源码如下。
#include <fcntl.h>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <unistd.h>
using namespace std; class Human{
private:
virtual void give_shell(){
system("/bin/sh");
}
protected:
int age;
string name;
public:
virtual void introduce(){
cout << "My name is " << name << endl;
cout << "I am " << age << " years old" << endl;
}
}; class Man: public Human{
public:
Man(string name, int age){
this->name = name;
this->age = age;
}
virtual void introduce(){
Human::introduce();
cout << "I am a nice guy!" << endl;
}
}; class Woman: public Human{
public:
Woman(string name, int age){
this->name = name;
this->age = age;
}
virtual void introduce(){
Human::introduce();
cout << "I am a cute girl!" << endl;
}
}; int main(int argc, char* argv[]){
Human* m = new Man("Jack", 25);
Human* w = new Woman("Jill", 21); size_t len;
char* data;
unsigned int op;
while(1){
cout << "1. use\n2. after\n3. free\n";
cin >> op; switch(op){
case 1:
m->introduce();
w->introduce();
break;
case 2:
len = atoi(argv[1]);
data = new char[len];
read(open(argv[2], O_RDONLY), data, len);
cout << "your data is allocated" << endl;
break;
case 3:
delete m;
delete w;
break;
default:
break;
}
} return 0;
}
之前没看过C++,花了点时间把C++的类与面向对象的一些知识学习了一下。
· 先看主函数,主函数中实现了分配堆块,释放堆块,以及从读入数据的功能。然后再看定义的几个类,Human是定义的一个基类,Man和Woman是定义的派生类,Human中public和protected定义的函数和变量可以在后面被子类继承和访问。但是定义的私有的give_shell虚函数,我们是无法在外部调用的,也无法通过子类来访问。只能通过Human类中定义的函数来调用。
我们这里需要一些预备知识:C++的类中有序函数的时候,会生成一个虚表vtable,编译器会生成一个vptr指针指向vtable(不管基类中有多少虚函数,只有一个_vptr指针指向vtable;多态继承时,继承了多个父类,相应就会又多个vtable)。对于共有成员变量来说,虚函数可以被子类同名函数重新调用,子类中有同名函数的时候,子类的vatble中指向基类虚函数的函数指针变为指向子类同名函数。对于私有成员来说,他的函数指针会保留在子类的vtable中(但是子类不是继承私有成员)。所以对于上面的源码来讲,Man类和Woman类的vtable中,introduce函数指针指不同,但是give_shell函数指针是相同的。我们可以再深入思考一下:vtable保留在哪里?通过IDA我们可以看出,vtable是保留在rodata段的。vtable保留着类的虚函数指针,它应该在编译前就完成,相应的它肯定不在代码段,bss段和data段分别保留的是未初始化和初始化后的局部变量,全局变量以及静态变量,所以vtable也不会在bss段或者data段。这样看来,vtable和字符串常量一样,保留在全局数据段是合理的。
这几张图片是从sakura师傅的博客取下来的,这里贴出来,大家可以看一下

对于这道题而言,类比C程序中利用的步骤,我们应改首先创建堆块,然后通过case3释放堆块,但使堆指针悬空,最后通过case2,读入文件内容,覆写类的函数指针,劫持数据流。
打开IDA,main函数的伪代码中可以得知,Man和Woman对象分配的堆的大小为0x18个字节。
我们首先通过调试,找到相应的虚表函数,要找到虚表函数,就要先找到Man的构造函数。

找到Man的构造函数之后进入,会发现调用Human的构造函数(因为Man作为子类要继承父类),所以这时候继续单步,可以在寄存器窗口看见调用give_shell函数的地址


找vtable的话,进入IDA,到rodata段。

Man类vtable表,give_shell的函数指针的地址为0x401570。现在要具体关注一下堆块内部的内存布局,以便在后面有效地覆盖函数指针。


0x40117a是give_shell函数地址,0x4012d2是introduce函数地址。
按照C++实例化类的内存分配的规则,类中有虚函数的时候,分配的对象的内存数据中,前8个字节是_vptr指针,这个指针指向vtable表,以后是成员变量的内存数据(这里应该只是成员变量的内存数据,不包括成员函数,所有函数都是定义在text段的)。
vptr指向的地址,是vtable表的地址,vtable表上存储的是虚函数的地址。
case2的时候填充的数据就应该是把前8个字节修改
case1调用intreduce函数的代码如下

v13和v14应该分别是Man对象和Woman对象的vptr指针,前面看到过,give_shell函数地址在0x401570地址处,我们向文件中写入0x401570-8的地址。
这里要注意的是,再次分配地址的时候,由于最后delete的是Woman对象的地址,第一次写入的是之前Woman的地址,第二次才是写入到Man对象地址的vtable表上。

终于是写完了这篇博客。。。
C++和堆的很多知识我绝对理解的不是很到位,是要不断学习的,但是这道题是看着博客,跌跌撞撞做出来了。写出来是为了加深印象,如果哪里有说的不对的地方,还希望路过的师傅们能过及时指出来,谢过师傅们了。
PWN——uaf漏洞学习的更多相关文章
- PWN二进制漏洞学习指南
目录 PWN二进制漏洞学习指南 前言 前置技能 PWN概念 概述 发音 术语 PWN环境搭建 PWN知识学习途径 常见漏洞 安全机制 PWN技巧 PWN相关资源博客 Pwn菜鸡小分队 PWN二进制漏洞 ...
- UAF漏洞学习
产生原因: UAF漏洞的成因是一块堆内存被释放了之后又被使用.又被使用指的是:指针存在(悬垂指针被引用).这个引用的结果是不可预测的,因为不知道会发生什么.由于大多数的堆内存其实都是C++对象,所以利 ...
- Hitcon 2016 Pwn赛题学习
PS:这是我很久以前写的,大概是去年刚结束Hitcon2016时写的.写完之后就丢在硬盘里没管了,最近翻出来才想起来写过这个,索性发出来 0x0 前言 Hitcon个人感觉是高质量的比赛,相比国内的C ...
- exim CVE-2017-16943 uaf漏洞分析
前言 本文由 本人 首发于 先知安全技术社区: https://xianzhi.aliyun.com/forum/user/5274 这是最近爆出来的 exim 的一个 uaf 漏洞,可以进行远程代码 ...
- 【pwn】学pwn日记——栈学习(持续更新)
[pwn]学pwn日记--栈学习(持续更新) 前言 从8.2开始系统性学习pwn,在此之前,学习了部分汇编指令以及32位c语言程序的堆栈图及函数调用. 学习视频链接:XMCVE 2020 CTF Pw ...
- [pwn基础]Pwntools学习
目录 [pwn基础]Pwntools学习 Pwntools介绍 Pwntools安装 Pwntools常用模块和函数 pwnlib.tubes模块学习 tubes.process pwnlib.con ...
- XSS漏洞学习笔记
XSS漏洞学习 简介 xss漏洞,英文名为cross site scripting. xss最大的特点就是能注入恶意的代码到用户浏览器的网页上,从而达到劫持用户会话的目的. 说白了就是想尽办法让你加载 ...
- Typecho-反序列化漏洞学习
目录 Typecho-反序列化漏洞学习 0x00 前言 0x01 分析过程 0x02 调试 0x03 总结 0xFF 参考 Typecho-反序列化漏洞学习 0x00 前言 补丁: https://g ...
- XXE漏洞学习笔记
XXE 参考文章 名称 地址 一篇文章带你深入理解漏洞之 XXE 漏洞 https://xz.aliyun.com/t/3357 Web Hacking 101 https://wizardforce ...
随机推荐
- 对比 Verilog 和 SystemVerilog 中的基本数据类型
作为引子,首先来看一段描述,该段介绍了SystemVerilog对比Verilog在RTL设计和建模时的新特性之一(logic数据类型),然后下文我再展开对比介绍Verilog和SystemVeril ...
- 腾讯云TKE-基于 Cilium 统一混合云容器网络(下)
前言 在 腾讯云TKE - 基于 Cilium 统一混合云容器网络(上) 中,我们介绍 TKE 混合云的跨平面网络互通方案和 TKE 混合云 Overlay 网络方案.公有云 TKE 集群添加第三方 ...
- UI自动化学习笔记- Selenium元素等待(强制等待、显示等待、隐式等待)
一.元素等待 1. 元素等待 1.1 什么是元素等待 概念:在定位页面元素时如果未找到,会在指定时间内一直等待的过程 意思就是:等待指定元素已被加载出来之后,我们才去定位该元素,就不会出现定位失败的现 ...
- Docker构建mysql主从
一.为什么要搭建主从架构呢 1.数据安全,可以进行数据的备份. 2.读写分离,大部分的业务系统来说都是读数据多,写数据少,当访问压力过大时,可以把读请求给到从服务器.从而缓解数据库访问的压力 3.故障 ...
- 12.5finally子句
要点提示:无论异常是否产生,finally子句总是会执行的. 有时候无论异常是否出现或者是否被捕获,都希望执行某些代码.java有一个finally子句,可以用来达到这个目的. 注意:使用finall ...
- 什么是 Acunetix 目标知识库
随着Acunetix 的最新更新,我们引入了一个称为目标知识库的新功能.每次扫描目标时,Acunetix 都会收集并存储有关它的信息.此信息包括构成站点结构的路径.表单的位置及其输入.Web 应用程序 ...
- 洛谷 P4008 [NOI2003]文本编辑器
先推广一下 求赞 我们考虑这样的一个问题 给你一个序列,要求你支持插入,删除,查询单点值 如果用数组,查询O(1),插入删除最坏O(n) 如果用链表,插入删除O(1),查询最坏O(n) 如果用平衡树- ...
- 何为“Secure Contexts”安全内容? 终于说明白了!
何为"Secure Contexts"安全内容? 终于说明白了! 看图说话 [途径1]:地址栏输入: edge://flags/ 按需设置选项后,重启浏览器即可. Allow ...
- yoyogo v1.7.6 增强程序优雅退出和K8s Readiness检查
YoyoGo (Go语言框架)一个简单.轻量.快速.基于依赖注入的微服务框架( web .grpc ),支持Nacos/Consoul/Etcd/Eureka/k8s /Apollo等 . 本次更新增 ...
- 【LeetCode】974. 和可被 K 整除的子数组
974. 和可被 K 整除的子数组 知识点:数组:前缀和: 题目描述 给定一个整数数组 A,返回其中元素之和可被 K 整除的(连续.非空)子数组的数目. 示例 输入:A = [4,5,0,-2,-3, ...