几乎都想要放弃了,感觉学了好久还是什么都不会,这个题好像很难的样子,有很多知识点需要补充一下:

1.【UAF】分配的内存释放后,指针没有因为内存释放而变为NULL,而是继续指向已经释放的内存。攻击者可以利用这个指针对内存进行读写。
2.【UAF利用】

(1)先搞出来一个迷途指针

(2)精心构造数据填充被释放的内存区域

(3)再次使用该指针,让填充的数据使eip发生跳转。

3.【malloc】

大于512字节的请求,是纯粹的最佳分配,通常取决于FIFO,就是最近使用过的。

小于64字节的请求,这是一个缓存分配器,保持一个快速的再生池块。

在这个两者之间的,对于大的和小的请求的组合,做的最好的是通过尝试,找到满足两个目标的最好的。

对于特别大的字节,大于128KB,如果支持的话,依赖于系统内存映射设备。

4.【虚函数】

虚函数,一旦一个类有虚函数,编译器会为这个类建立一张vtable。子类继承父类(vtable)中所有项,当子类有同名函数时,修改vtable同名函数地址,改为指向子类的函数地址,子类有新的虚函数时,在vtable中添加。记住,私有函数无法继承,但如果私有函数是虚函数,vtable中会有相应的函数地址,所有子类可以通过手段得到父类的虚私有函数。

接下来分析函数:

 #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", );
Human* w = new Woman("Jill", ); size_t len;
char* data;
unsigned int op;
while(){
cout << "1. use\n2. after\n3. free\n";
cin >> op; switch(op){
case :
m->introduce();
w->introduce();
break;
case :
len = atoi(argv[]);
data = new char[len];
read(open(argv[], O_RDONLY), data, len);
cout << "your data is allocated" << endl;
break;
case :
delete m;
delete w;
break;
default:
break;
}
} return ;
}

如下:

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;
}

类human有虚函数,所以有一个vtable,这个vtable中记录了类中所有虚函数的函数指针,即包括give_shell和introduce两个函数的函数指针。接着往下看:

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;
}
};

这两个类函数,继承了hunam函数,实现了各自的Introduce,这两个类都会继承父类的vtable,vtable中introduce的函数指针被替换成了他们自己的函数地址。

接下来再看主函数:

int main(int argc, char* argv[]){
Human* m = new Man("Jack", );
Human* w = new Woman("Jill", ); size_t len;
char* data;
unsigned int op;
while(){
cout << "1. use\n2. after\n3. free\n";
cin >> op; switch(op){
case :
m->introduce();
w->introduce();
break;
case :
len = atoi(argv[]);
data = new char[len];
read(open(argv[], O_RDONLY), data, len);
cout << "your data is allocated" << endl;
break;
case :
delete m;
delete w;
break;
default:
break;
}
} return ;
}

主函数是一个case选择:

1.调用两个类函数

2.分配data空间,从文件名为argv[2]中读取长度为argv[1]的字符到data部分。

3.释放对象

这里如果是先执行3再执行2,那么把对象空间释放并且把指针置NULL却又去引用了,就触发了UAF漏洞。那么如何操纵被释放的空间呢?可以看到在case2中,是从文件名为argv[2]中读取长度为argv[1]的字符到data部分。利用前面所述UAF漏洞,data在分配空间的时候就分配到了case3中被释放的空间。如果我们能够把introduce函数的指针覆盖为give_shell的指针,那么就可以在接着执行1,调用shell了。

可以看到程序中分配了24个字节,接着片下看:

此处调用了man函数,一步步跟进去,发现了give_shell地址

返回去看一下,发现了human的vtable,往上走一点,又发现了man的vtable:

下面的地址点进去后:分别是give_shell地址和introducd地址

而human中give_shell地址和与man一致,但introduce却不同:

接着分析swich函数,选择1,调用introduce函数,

补充:

当类中有虚函数的时候,编译器会为类插入一个我们看不见的数据并建立一个表。这个表就是虚函数表(vtbl),那个我们看不见的数据就是指向虚函数表的指针——虚表指针(vptr)。虚函数表就
是为了保存类中的虚函数的地址。我们可以把虚函数表理解成一个数组,数组中的每个元素存放的就是类中虚函数的地址。当调用虚函数的时候,程序不是像普通函数那样直接跳到函数的代码处,而
是先取出vptr即得到虚函数表的地址,根据这个来到虚函数表里,从这个表里取出该函数的地址,最后调用该函数。所以只要不同类的vptr不同,他对应的vtbl就不同,不同的vtbl装着对应类的
虚函数地址,这样虚函数就可以完成它的任务了。

于是根据上图可以分析出v13是vptr,再由

v13再转换为指针,加上8为introduce的第一个指针。然后调用introduce。

我们漏洞利用的思路是调用introduce的时候,换成give_shell地址调用。

所以往下分析:

前面我们分析了give_shell的地址和introduce的地址give_shell的地址+8=introduce的地址。即give_shell=introduce-8,(give_shell=v13+8-8),如果想调用introduce时调用成give_shell就要将introduce的地址减去8指向give_shell地址

如图,如果我们把vtable指向图中地址等于v13,那么v13+8调用introduce时不就调用成了give_shell

“在C++中,如果类中有虚函数,那么它就会有一个虚函数表的指针__vfptr,在类对象最开始的内存数据中。之后是类中的成员变量的内存数据。”

那么根据这句话所说,这个程序在case2中读取数据的填充到data空间的时候,开始的八字节就是vtable。之后是类的数据。

所以利用过程如下:

uaf@ubuntu:~$ python -c "print '\x68\x15\x40\x00\x00\x00\x00\x00'" >/tmp/poc
uaf@ubuntu:~$ ./uaf /tmp/poc

得到:

选1先释放空间获得地址,选2读取数据填充到data空间,之后选择2是类的数据。然后选1调用函数

参考链接 :http://blog.csdn.net/qq_20307987/article/details/51511230

pwnable.kr uaf之wp的更多相关文章

  1. 【pwnable.kr】 uaf

    目测是比较接近pwnable的一道题.考察了uaf(use after free的内容),我觉得说白了就是指针没有初始化的问题. ssh uaf@pwnable.kr -p2222 (pw:guest ...

  2. pwnable.kr的passcode

    前段时间找到一个练习pwn的网站,pwnable.kr 这里记录其中的passcode的做题过程,给自己加深印象. 废话不多说了,看一下题目, 看到题目,就ssh连接进去,就看到三个文件如下 看了一下 ...

  3. [pwnable.kr]Dragon

    0x00: dragon 是一个UAF漏洞的利用. UseAfterFree 是堆的漏洞利用的一种 简单介绍 https://www.owasp.org/index.php/Using_freed_m ...

  4. Pwnable.kr

    Dragon —— 堆之 uaf 开始堆的学习之旅. uaf漏洞利用到了堆的管理中fastbin的特性,关于堆的各种分配方式参见堆之*bin理解 在SecretLevel函数中,发现了隐藏的syste ...

  5. pwnable.kr之brainf*ck

    pwnable.kr之brainf*ck 今天又是被难倒的一天Orz,个人感觉pwnable.kr上的题都比较剑走偏锋,仔细做过去,一定会有很大的收获. 不多说了,今天看的是第二关的第一道题:brai ...

  6. pwnable.kr之simple Login

    pwnable.kr之simple Login 懒了几天,一边看malloc.c的源码,一边看华庭的PDF.今天佛系做题,到pwnable.kr上打开了simple Login这道题,但是这道题个人觉 ...

  7. pwnable.kr bof之write up

    这一题与前两题不同,用到了静态调试工具ida 首先题中给出了源码: #include <stdio.h> #include <string.h> #include <st ...

  8. pwnable.kr col之write up

    Daddy told me about cool MD5 hash collision today. I wanna do something like that too! ssh col@pwnab ...

  9. pwnable.kr brainfuck之write up

    I made a simple brain-fuck language emulation program written in C. The [ ] commands are not impleme ...

随机推荐

  1. pycharm 断点调试

    转自; https://blog.csdn.net/chenggong2dm/article/details/9368641 PyCharm 作为IDE,断点调试是必须有的功能.否则,我们还真不如用纯 ...

  2. PHP 遍历一个文件夹下所有文件和子文件夹的方法

    话不多说,直接上代码 <?php function my_dir($dir) { $files = []; if(@$handle = opendir($dir)) { while(($file ...

  3. 通过split命令分割大文件

    场景 线上出了问题,我需要去查找log来定位问题,但是由于线上数据量庞大,这些log文件每过一个小时就会自动回滚一次,尽管如此,有的log文件依然达到了五六g以上的大小. 对于这种巨大的log文件,常 ...

  4. UltraEdit配置C++开发环境

    1.下载UltraEdit软件安装 2.下载MinGW库安装 3.在系统设置环境变量,在path里加上你的MinGW\bin目录即可. 4.进入UltraEdit软件进行如下配置: 菜单栏---高级- ...

  5. rhel7使用centos7yum组件

    1)rpm -qa|grep yum --查看已安装的yum组件包 2)rpm -e 包名 --nodeps --卸载包 3)下载安装以下组件包: 使用rpm -ivh yum-* yum-3.4.3 ...

  6. MY $MYVIMRC

    set nocompatiblesource $VIMRUNTIME/vimrc_example.vim"source $VIMRUNTIME/mswin.vim"behave m ...

  7. 生成hprof文件,用MAT进行分析

    生成hprof文件可以在DDMS选中进程点击窗口左上角的"dump hprof file"按钮来直接生成,也可以通过在程序加代码中来生成 代码2: void generateHpr ...

  8. 洛谷P1724 东风谷早苗

    题目描述 在幻想乡,东风谷早苗是以高达控闻名的高中生宅巫女.某一天,早苗终于入手了最新款的钢达姆模型.作为最新的钢达姆,当然有了与以往不同的功能了,那就是它能够自动行走,厉害吧(好吧,我自重).早苗的 ...

  9. 使用SpringBoot-JPA进行自定义的保存及批量保存

    更多精彩博文,欢迎访问我的个人博客 说明 SpringBoot版本:2.1.4.RELEASE java版本:1.8 文中所说JPA皆指spring-boot-starter-data-jpa 使用J ...

  10. QT+信号和槽函数_自定义槽函数_一个信号对应多个槽函数

    以下的代码里面有自定义槽函数的内容,同时也有信号实现的函数: #ifndef MAINWIDGET_H #define MAINWIDGET_H #include <QWidget> #i ...