转载:
https://blog.youkuaiyun.com/u010651541/article/details/76165216
本篇主要是自己学习笔记,快速读了下泉哥的《漏洞战争》的读书笔记。这里只涉及漏洞产生原理,即漏洞是怎么写出来。至于怎么分析0Day,怎么写代码执行的exp,后续将做深入研究。
C/C++的代码执行漏洞,很多时候是劫持程序的控制流。具体来说:对于C程序,一般是控制函数的返回地址,让程序跳转到我们指定的地方执行。对于C++程序,除了覆盖函数返回地址外,还可以覆盖虚函数表,在调用虚函数的时候,程序将到指定内存处执行。
一、栈溢出漏洞
栈溢出漏洞原理十分简单,只需要了解C语言函数调用时程序的内存结构即可。一句话总结,可控的参数覆盖了函数的返回地址。
#include<stdio.h>
#include<string.h>
void vulfunc(char* str){
char src[10];
strcpy(src,str);
}
int main(){
char* str="AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
vulfunc(str);
return;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
执行程序,程序将crash,并触发Segmentation fault错误。
二、堆溢出漏洞
堆溢出漏洞和栈溢出漏洞原理类似,但是比栈溢出复杂得多,后面单独介绍原理。
#include<stdlib.h>
#include<string.h>
int main(int argc,char* argv[]){
char* first,second;
first = malloc(100);
second = malloc(12);
if(argc>1){
strcpy(first,argv[1]);
}
free(fisrt);
free(second);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
同样程序执行时,触发Segmentation fault错误而crash掉。
三、整数溢出
C/C++非内存安全行语言,当输入的整数超出整数的取值范围时,编译器并不会报出错误,但是程序执行时,可能造成严重的安全后果。不过最终导致代码执行,还是归因到栈溢出或者堆溢出。
//栈的整数溢出
#include<stdio.h>
#include<string.h>
int main(int argc,char* argv){
int i;
char buf[8];
unsigned short int size;
char overflow[65550];
memset(overflow,65,sizeof(overflow));
printf("please input size:\n");
scanf("%d",&i);
size =i;
printf("size:%d",size);
printf("i:%d",i);
if(size > 8){
return -1;
}
//stack overflow
memcpy(buf,overflow,i);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
上面,size类型为unsigned int,最大取值为65535,当超过这个值时,截断导致越过size检查,然后,在memcpy()函数函数中造成栈溢出,程序crash,可能导致代码执行。堆上的整数溢出同理,只是溢出对象是堆数据,导致覆盖的时候后面的堆结构。
四、格式化字符串漏洞
#include<stdio.h>
#include<string.h>
int main(int argc,char* argv[]){
char buff[1024];
strncpy(buff,argv[1],sizeof(buff)-1);
printf(buff);
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
当输入参数为mmm%s或者ttt%x时,程序将产生意向不到的结果。printf()函数的基本形式为:
printf("格式化字符串","参数列表");
- 1
这样,当输入ttt%s时,%s不会被当做数据,而是被当成了格式化字符串处理,程序会读取栈上argv[1]后面一个栈上的数据,造成了内存数据的泄露。当输入中包含很多这样的字符时,将可以遍历栈上的数据。
五、UAF漏洞
#include<stdio.h>
#define size 32;
int main(int argc,char** argv){
char *buf1;
char *buf2;
buf1=(char*) malloc(size);
printf("buf1:0x%p\n",buf1);
free(buf1);
//buf1=null;
buf = (char*)malloc(size);
printf("buf2:0x%p\n",buf2);
memset(buf2,0,size);
printf("buf2:%d\n",*buf2);
printf("After free\n");
strncpy(buf1,"hack",5);
printf("buf2:%s\n\n",buf2);
free(buf2);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
理解这个漏洞的关键是系统对于内存的管理,(后面会深入分析)程序所能拥有的栈内存空间总是有限的,很多需要堆内存,对内存虚拟的内存空间中,堆内存是向上增长的。所以,上面buf1在被free之后,内存管理在再次分配buf2的时候,会认为buf1地址处的内存已经没有用,会分配给buf2,即buf2地址指向了之前buf1地址处。关键是,没有做buf1=NULL的操作,导致buf1成为”野指针”,依然有效。这样,后续如果能够操作到buf1的指针,就可以长生意想不到的结构。如果buf1处为执行指令,则导致了CE。
class CTest{
int m=1;
public:
virtual void vFunc1();
virtual void vFunc2();
int getM(){
return m;
}
}
void main(){
CTest test; // 实例化;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
在C++代码中,UAF导致CE更加普遍,以为我们常常需要通过new来创建一个对象。如果后面再free()了对应的对象指针后(体现free),没有对指针置NULL。那么攻击者可能通过”占坑式”来让一个指针指向对象的地址,然后利用此指针修改对象的虚函数表。此时,如果对象再次被使用,尤其是虚函数被调用时(体现use after),将导致CE。
六、Double Freee
#include<stdio.h>
int main(int argc,char **argv){
void * p1,p2,p3;
p1=malloc(100);
printf("malloc:%p\n",p1);
p2=malloc(100);
printf("malloc:%p\n",p2);
p3=malloc(100);
printf("malloc:%p\n",p3);
pritf("free p1\n");
free(p1);
pritf("free p3\n");
free(p3);
pritf("free p2\n");
free(p2);
printf("Double free\n");
free(p2);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
理解这个漏洞并不那么容易,如何导致代码执行也会后面单独来研究,这里只吃别人吃剩的骨头,记录下结论,后面一定要自己研究!!!!!程序释放了p1,p3之后,在释放p2时,会发生堆内存的合并动作,将改变原有的堆头信息及前后向指针。再次释放时,free动作其实应该是引用了之前的地址,所以导致了崩溃。这里,后面研究时,应该要研究系统对堆内存的回收过程。按照泉哥的结论,这根本上是个UAF漏洞,UAF漏洞是比较容易理解的,所以理解内存回收应该是关键。
七、数组越界
数组越界通常包括读越界和写越界,写越界会造成溢出漏洞。
#include<"stdio.h">
int main(){
int index;
int array[3]={111,222,333};
printf("please input index:\n");
scanf("%d",&index);
printf("array[%d]=%d\n",index,array[index]);
//写越界,造成溢出;
a[index]=233;
return 0;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
上面在读取时会造成数组读的越界,赋值时造成(栈)溢出。
以上总结了常见的漏洞类型,其中还有很多知识点需要单独补充:
- 堆溢出原理分析;
- linux对数据分配和回收的管理;
- double free漏洞深入分析;
初次之外,后续要研究的包括:ROP,堆喷,地址随机化绕过,这些属于漏洞利用的知识。