内存攻防技术
内存的攻防技术主要分为
一、缓冲区溢出漏洞攻击
缓冲区溢出buffer overflow是由于程序缺乏对缓冲区的边界条件检查而引起的一种异常行为,通常是程序向缓冲区中写数据,但内容超过了程序猿设置的缓冲区边界,从而覆盖了相邻的内存区域,造成覆盖程序中的其他变量甚至影响控制流的敏感数据,造成程序的非预期行为。
一般根据缓冲区溢出的内存位置的不同,缓冲区溢出又分为栈溢出(Stack Overflow)和堆溢出(Heap Overflow)
二、栈溢出利用原理
程序执行过程中的栈,是由操作系统创建与维护的,同时也支持了程序内的函数调用功能,在进行函数调用的时候,程序会将返回地址压入栈中,跳转到函数地址执行,执行完函数调用后,再通过ret指令从栈中弹出返回地址,装载到EIP指令寄存器,从而程序继续运行。
栈溢出发生在程序向位于栈中的内存地址写数据时,栈溢出的利用方式,通常有:
A、覆盖缓冲区附近的程序变量,改变程序的执行流程和结果
B、覆盖栈中保存的函数返回地址,修改为攻击者指定的内存地址,当程序返回时可以任意执行代码。(这个是主流方式)
C、覆盖某个函数指针或程序异常处理结构,只要溢出之后的目标函数或异常函数处理例程被执行,同样可以让程序流程转到任意地址。
三、堆溢出利用原理
不同于栈,堆是程序运行时动态分配的内存,用户通过malloc,new等函数申请的内存,通过返回的起始地址指针对分配的内存进行操作,使用完成后需要用free,delete等函数释放这部分内存,否则会造成内存泄露。当像堆块写入数据超长的时候,堆块溢出会写入到相邻空闲的堆块地址,出现一个指针两个堆块的情况,但此空闲堆分配出去再回收后,空闲前指针会指向已分配的内存地址,造成向事先已分配的内存地址写入数据能够写入到另一内存地址中去。
四、缓冲区溢出利用的限制条件
首先要考虑到程序用来接收输入的缓冲区空间大小
接着要通过逆向分析或者源码解析来理解程序逻辑,找出符合程序逻辑的输入数据样式,复杂的格式还需要通过编码来实现
最后在输入数据中放置shellcode时,需要考虑坏字符,这些字符可能会导致缓冲区不满足程序逻辑,或是在通过函数调用导致输入被截断。
五、攻防两端的对抗博弈
VS2003开始程序编译的时候加入了GS选项,用来防止缓冲区溢出,从XP SP2开始全面支持这些选项。加强了堆块指针操作验证将缓冲区变量置于栈帧底部,切在环球车与栈指针(EBP)之间插入了一个随机变化的cookie,在函数返回的时候验证cookie是否被改变从而判断是否发生溢出,决定是否用改地址返回。对此攻击方首先会溢出覆盖栈中SEH结构(Structured Exception Handling结构化异常处理),然后在程序中检查cookie之前触发异常,那么将完全绕过这个cookie,去执行被改写的SEH。
随后防守方在链接选项中增加了SafeSEH,用句柄的方式来验证SEH结构是合法的,攻击方采用利用进程中未启用的SEH模块,将其修改后指向这些模块中的PPR(POP POP RET)代码块中
最后防守方提出SEHOP,在windows server 2008,Vista SP1中引入该技术,这一技术在程序运行时验证整个SEH的完整性。
内存攻击的主要原因是程序错误的把外部获取的数据当成了指令执行,而后数据执行保护改变了这一情况。数据执行保护将程序数据段所在的内存页面(堆栈)属性设置为NX(No eXecute),当程序执行数据段所在的内存页面之时就会直接报错。在XP-SP2的时候提出了软件DEP,随后在CPU升级的基础上实现了硬件DEP。
针对于DEP攻击者又找到了办法--return to libc,串联已经加载的系统库函数中以ret结尾的代码块(gadget),从而实现关闭进程DEP保护。
串联库函数以外的第三方模块实现关闭DEP,比如Java runtime Environment