目录
一,前言
缓冲区溢出(buffer-overflow)是一种非常普遍、同时非常危险的漏洞,在各种操作系统、应用软件中广泛存在。缓冲区溢出攻击是利用缓冲区溢出漏洞所进行的攻击,轻则可以导致程序失败、系统关机等,重则可以利用它执行非授权指令,甚至获取系统特权,从而进行其它的非法操作。缓冲区攻击有栈溢出、堆溢出、格式化字符串漏洞、整形变量溢出等。以堆栈溢出为代表的缓冲区溢出已成为最为普遍的安全漏洞。由此引发的安全问题比比皆是。为了尽可能避免缓冲区溢出漏洞被攻击者利用,现今的编译器设计者已经开始在编译器层面上对堆栈进行保护。
二,堆栈溢出原理
在计算机里,堆栈是内存里的一段区域。堆一般由程序员分配释放,如果程序员不释放,程序结束时可能由操作系统回收,分配方式类似于数据结构中的链表;栈由操作系统自动分配释放,存放函数的参数值、局部变量、返回地址等,分配方式类似于数据结构中的栈。以堆栈溢出为代表的缓冲区溢出已经成为最普遍的安全漏洞,由此引发的安全问题比比皆是。堆栈溢出的原因一般有以下几种:
1,函数调用层次太深。函数递归调用时,系统要在栈中不断保存函数调用时的现场和产生的变量,,如果递归调用太深,就会造成栈溢出,这是递归无法返回。再者,当有函数调用层次过深时也可能导致栈无法容纳这些调用的返回地址而造成栈溢出。
2,动态申请空间(可参考下文了解更多)C/C++动态内存开辟详解(含常见错误以及经典面试题)_利刃Cc的博客-CSDN博客_c++动态开辟空间使用之后没有释放。由于c语言中没有垃圾资源自动回收机制,因此,需要程序主动释放已经不再使用的动态地址空间。申请的动态空间使用的是堆空间,动态空间使用不会造成堆溢出。
3,数组访问越界。C语言没有提供数组下标越界检查,如果在程序中出现数组下标访问超出数组范围,在运行过程中可能会内存访问错误。
4.指针非法访问。指针保存了一个非法的地址,通过这样的指针访问所指向的地址时会产生内存访问错误。
堆溢出:不断的new一个对象,一直创建新的对象
栈溢出:死循环或者是递归太深的原因,可能太大或者没有终止。
通常堆栈溢出是指调用堆栈溢出,溢出是指这个数据结构的满溢,不能存放更多数据,其他的数据结构也会遇到这种情况。即使数据结构并非固定容量,而是可扩展的,在有限的内存空间下仍是有满溢的机会。
在一些高级语言中,类似python, java, go等,有一些机制用于防止栈溢出,比如,python默认的递归深度是1000,当递归调用超过这个深度后就会引发异常。此外,编译器层面上也有对堆栈进行保护,其中最著名的是Stack Guard和Stack-smashing Protectection。在操作系统的层面上,为了减少堆栈溢出带来的危害,还有类似于地址空间随机化的机制。
三,操作系统内置的安全机制
Windows:
1,DEP(data execution prevention):使得系统能够标记一个或多个内存页的属性为不可执行(意味着代码不能在该内存页中执行),有助于缓解缓冲区溢出攻击的危害。
2,ASLR(address space layout randomization):加载地址随机化机制
开启方式:vs开发环境中的链接器下的高级选项中可以设置映像随机化,堆栈随机化,PEB和TEB随机化
3,GS机制:缓冲区安全检查,编译器会为每个函数调用的入口增加额外的随机数(被称为canary),它位于EBP之前,同时在data内存区存放该随机数的副本,当函数发生溢出时,它会被覆盖,函数在返回之前会比较data区中的副本的值和栈帧的真实值,如果两者不同,则触发异常处理流程。
开启方式:vs开发环境中的c/c++下的代码生成中的开启缓冲区安全检查
Linux:
1,NX机制(no-execute):同Windows上的DEP功能相似
关闭NX机制(于是JMP与ESP无法使用);编译时增加 -z execstack; 开启NX机制:编译时增加 -z noexecstack(默认开启)
2,PIE机制(position-independent-executables):位置独立的可执行区域,同Windows系统的ASLR功能相似,一般同NX机制配合使用,能够有效组织在堆栈上运行恶意代码,PIE有三种工作模式:1,工作模式0:关闭进程地址空间随机化
2,工作模式1:开启mmap机制,stack和vdso的地址随机化
3,工作模式2:在1的基础上,增加堆地址随机化
3,Cannary栈保护:同Windows上的GS机制,一种预防缓冲区溢出的手段,
开启cannary栈保护,在编译时增加 -fstack-protector(只为含char数组的函数加入保护代码)和-fstack-protector-all(为所有函数加入保护代码)
关闭cannary保护,在编译时增加 -fno-stack-protector(默认不开启)
4,RelRo机制:设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对GOT(Global Offset Table)攻击。
全部开启RelRo机制:在编译时增加 -z now
部分开启RelRo机制:在编译时增加 -z lazy(默认配置)
关闭RelRo机制: 在编译时增加 -z norelro