UAF
UAF 又名 use after free
意思就是使用了已经被free过的堆
- 如果内存被释放了之后 这个指针如果没有被设为NULL 之后又被使用了就又很大的可能可以继续正常运行
- 当又有程序对那片地方进行了修改,在继续运行就会发生很奇怪的问题
简单的例子
#include <stdio.h>
#include <stdlib.h>
typedef struct name {
char *myname;
void (*func)(char *str);
} NAME;
void myprint(char *str) { printf("%s\n", str); }
void printmyname() { printf("call print my name\n"); }
int main() {
NAME *a;
a = (NAME *)malloc(sizeof(struct name));
a->func = myprint;
a->myname = "I can also use it";
a->func("this is my function");
// free without modify
free(a);
a->func("I can also use it");
// free with modify
a->func = printmyname;
a->func("this is my function");
// set NULL
a = NULL;
printf("this pogram will crash...\n");
a->func("can not be printed...");
}
运行结果
this is my function
I can also use it
call print my name
this pogram will crash...
[1] 38738 segmentation fault (core dumped) ./use_after_free
发现当a的指针没有变为NULL时指向的那片地址还是可以继续使用
- 注意
- uaf的前提这个变量最好是全局变量,存储到bss段上的时候才更容易的使用,因为在一个函数里面uaf 只会存储在栈上就不能一直使用
看一段代码
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
struct note {
void (*printnote)();
char *content ;
};
struct note *notelist[5];
int count = 0;
void print_note_content(struct note *this){
puts(this->content);
}
void add_note(){
int i ;
char buf[8];
int size ;
if(count > 5){
puts("Full");
return ;
}
for(i = 0 ; i < 5 ; i ++){
if(!notelist[i]){
notelist[i] = (struct note*)malloc(sizeof(struct note));
if(!notelist[i]){
puts("Alloca Error");
exit(-1);
}
notelist[i]->printnote = print_note_content;
printf("Note size :");
read(0,buf,8);
size = atoi(buf);
notelist[i]->content = (char *)malloc(size);
if(!notelist[i]->content){
puts("Alloca Error");
exit(-1);
}
printf("Content :");
read(0,notelist[i]->content,size);
puts("Success !");
count++;
break;
}
}
}
void del_note(){
char buf[4];
int idx ;
printf("Index :");
read(0,buf,4);
idx = atoi(buf);
if(idx < 0 || idx >= count){
puts("Out of bound!");
_exit(0);
}
if(notelist[idx]){
free(notelist[idx]->content);
free(notelist[idx]);
puts("Success");
}
}
void print_note(){
char buf[4];
int idx ;
printf("Index :");
read(0,buf,4);
idx = atoi(buf);
if(idx < 0 || idx >= count){
puts("Out of bound!");
_exit(0);
}
if(notelist[idx]){
notelist[idx]->printnote(notelist[idx]);
}
}
void magic(){
system("cat /home/hacknote/flag");
}
void menu(){
puts("----------------------");
puts(" HackNote ");
puts("----------------------");
puts(" 1. Add note ");
puts(" 2. Delete note ");
puts(" 3. Print note ");
puts(" 4. Exit ");
puts("----------------------");
printf("Your choice :");
};
int main(){
setvbuf(stdout,0,2,0);
setvbuf(stdin,0,2,0);
char buf[4];
while(1){
menu();
read(0,buf,4);
switch(atoi(buf)){
case 1 :
add_note();
break ;
case 2 :
del_note();
break ;
case 3 :
print_note();
break ;
case 4 :
exit(0);
break ;
default :
puts("Invalid choice");
break ;
}
}
return 0;
}
代码只有125行是个很简单的笔记本功能
有一个magic
函数在代码中没有被使用是用来读取/home/hacknote/flag
内容的
void del_note(){
char buf[4];
int idx ;
printf("Index :");
read(0,buf,4);
idx = atoi(buf);
if(idx < 0 || idx >= count){
puts("Out of bound!");
_exit(0);
}
if(notelist[idx]){
free(notelist[idx]->content);
free(notelist[idx]);
puts("Success");
}
}
可以看到notelist[idx]
没有被设置为NULL
所以按照之前的问题我们仍然可以调用
void print_note_content(struct note *this){
puts(this->content);
}
这个函数
调用了之后发现存在错误调试一下
因为free了之后那块内存为0了所以调用的时候就会报错,我们也无法直接在哪个地址上写上我们需要的函数,所以我们只能通过曲线救国的方式来实现,再
add_note
一次修改那片内存的数据为magic
地址就可以了先看一下
/home/hacknote/flag
的数据然后运行
成功的运行了
magic
函数