前言
在编写漏洞利用代码的时候,需要特别注意目标进程是否开启了NX
、PIE
等机制,例如存在NX
的话就不能直接执行栈上的code
,存在PIE
的话各个系统调用的地址就是随机化的。Linux中的保护机制包括canary
,NX(no-execute)
,PIE(position-independent executables)
,RELRO(read only relocation)
,FORTIFY
等等,暂时没写关于内核安全防护的内容,以后再补充。检查可执行文件属性可使用checksec
。
canary
栈保护。栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址从而控制程序流的执行。当启用栈保护后,函数开始执行的时候会先往栈里插入一个特殊的value
值,当函数返回的时候会验证value
值是否变化,如果发生变化就停止程序运行。攻击者在覆盖返回地址的时候往往也会将value
值给覆盖掉,导致栈保护检查失败而终止程序的执行。这个特殊的value
值我们称之为canary
。
编译时控制是否开启栈保护及程度的指令:
1 | gcc -o test test.c // 默认情况下,不开启Canary保护 |
NX:no-execute
堆栈不可执行。将数据所在内存页标识为不可执行,当程序执行shellcode
中的指令时,CPU就会抛出异常,而不是去执行恶意指令。Linux中的NX
与Windows下的DEP
工作原理类似,DEP
工作原理如下:
控制是否开启NX
的指令如下:
1 | gcc -o test test.c // 默认情况下,开启NX保护 |
注:在Windows下,类似的概念为DEP:Data Execution Prevention
(数据执行保护)
PIE:position-independent executables
位置独立的可执行区域。NX
和PIE
常常同时工作,这样使得在利用缓冲溢出和移动操作系统中存在的其他内存崩溃缺陷时采用ROP
技术变得难得多。Windows中的类似机制叫做ASLR:address space layout randomization
(内存地址随机化机制),有以下三种情况:
1 | 0 - 表示关闭进程地址空间随机化 |
Linux下关闭PIE
的命令如下:
1 | sudo -s echo 0 > /proc/sys/kernel/randomize_va_space |
编译时的控制指令如下:
1 | gcc -o test test.c // 默认情况下,不开启PIE |
RELRO:read only relocation
只读重定位。在Linux系统中,数据可以写的存储区就会是攻击的目标,尤其是存储函数指针的区域。 所以从安全防护的角度来说,尽量减少可写的存储区域对安全会有极大的好处。RELRO
让加载器将重定位表中加载时解析的符号标记为只读或在程序启动时就解析并绑定所有动态符号,这减少了GOT
覆写攻击的面积。
RELRO
可分为:
1 | Partial RELRO(部分RELRO) //开启Partial RELRO后GOT表是可写的 |
编译时的控制指令如下:
1 | gcc -o test test.c // 默认情况下,是Partial RELRO |
Fortity
一种非常轻微的检查,用于检查是否存在缓冲区溢出的错误。适用情形是程序采用大量的字符串或者内存操作函数,如memcpy
,memset
,stpcpy
,strcpy
,strncpy
,strcat
,strncat
,sprintf
,snprintf
,vsnprintf
,gets
以及宽字符的变体。开启fortity
检查后会替换strcpy
等危险函数,由于开销较大,所以默认不开启。
1 | gcc -D_FORTIFY_SOURCE=1 //仅在编译时进行检查 (特别像某些头文件 #include <string.h>) |
上述两图分别为不启用和启用fortity
的截图。对比发现,启用fortity
后,程序在执行strcpy
函数时,运行了__strcpt_chk
函数,这个函数用来检查是否溢出。检查通过后,这个函数会调用strcpy
函数。
总结
这些安全保护措施极大程度上杜绝了恶意程序的攻击,但大部分情况下有一定缺陷、或需要耗费大量资源。这些保护机制仍需要程序员在操作内存时注意程序的安全问题,如需要严格检查不可信的输入。关于如何绕过这些防护机制,待续。。。