在黑客圈子中,基于内存攻击技术的攻击手段在随着时代的变化而不断发展着,内存攻击是指通过利用软件的安全漏洞,构造恶意的输入,从而使正常程序造成拒绝服务或者是远程获得控制权,内存攻击技术中最先登上历史舞台的就是缓冲区溢出漏洞,时至今日能够被广泛利用的 60% 以上的高危漏洞(CVE)都属于缓冲区溢出,接下来我将总结缓冲区溢出的相关知识点。
缓冲区溢出: 缓冲区溢出(Buffer Overflow),分为栈溢出与堆溢出,此类漏洞的原理是,程序由于缺乏对缓冲区的边界进行合理化的检测而引起的一种异常行为,通常是程序存在过滤不严格的输入点,通过这些输入点攻击者可以向程序中写入超过了程序员预先定义好的缓冲边界,从而覆盖了相邻的内存区域,造成程序中的变量覆盖,甚至控制EIP指针,从而造成程序的非预期行为,而像 C/C++ 程序中本身就缺乏内在的内存安全分配与管理,因此缓冲区溢出漏洞大部分都出现在编译型语言中。
栈溢出: 栈溢出是缓冲区溢出中最为常见的一种攻击手法,其原理是,程序在运行时栈地址是由操作系统来负责维护的,在我们调用函数时,程序会将当前函数的下一条指令的地址压入栈中,而函数执行完毕后,则会通过ret指令从栈地址中弹出压入的返回地址,并将返回地址重新装载到EIP指令指针寄存器中,从而继续运行,然而将这种控制程序执行流程的地址保存到栈中,必然会给栈溢出攻击带来可行性。
堆溢出: 除了栈溢出还有一个堆溢出,不同于栈溢出的是,堆是在程序运行时动态的分配的,以C/C++为例,当程序员需要堆空间时,可通过new(),calloc(),malloc()等函数来进行动态的申请,申请后会返回一个内存指针,我们可以通过返回的内存指针对分配的内存进行各种操作,但在使用完堆空间时必须手动的释放,由于堆在内从中的分配位置不固定,大小比较自由,多次申请释放后可能会让内存更加凌乱,轻者内存泄漏,重者可对程序的安全造成致命的威胁。
堆空间虽然比较自由,但在分配时也会分配连续的内存空间,如果向堆区中写入了超出其长度的内容,就会导致数据溢出,并覆盖到堆块后方的相邻空闲堆块,而后方的堆区中可能存放着指向下一个堆区的指针,如果该指针被恶意控制的话,就会出现很恶劣的后果,虽然危险,但是堆溢出的利用方式灵活性非常高,而且利用起来非常的困难,这里我们不讨论这种溢出。
攻防双方的博弈
在大致弄清楚缓冲区溢出攻击之后,我这里总结了攻防双方的对抗博弈过程,攻击者与防御者的对抗博弈斗争从来都没有停止过,在大环境下防御始终落后于攻击,但不论如何正是因为有攻防双方的对抗,才使得系统安全水平呈现螺旋式上升的态势,如下是攻防双方的对抗过程总结:
首先在当前的环境下,微软的内存保护机制大致分为以下几种:
- 堆栈缓冲区溢出检测保护 GS (编译器)
- 安全结构化异常处理保护 Safe SEH
- 堆栈 SEH 覆盖保护 SEHOP
- 地址空间布局随机化保护 ASLR
- 堆栈数据执行保护 DEP
堆栈缓冲区溢出检测保护 GS (编译器)
保护原理: 该保护是通过编译器进行限制的,GS选项是微软堆栈检测仪概念的具体实现:从 Visual Studio 系列的编译器上就加入了GS保护机制且默认开启,操作系统从 WindowsXP 开始就已经全面支持该选项了。
其原理是,将缓冲区变量置于栈帧的底部,且在缓冲区与栈指针(EBP)之间插入一个随机化的 Cookie ,在函数返回时验证该 Cookie 是否发生了改变,如果发生了改变,则说明恶意代码覆盖了该区域,从而决定不在使用该返回地址。
绕过措施: 实际上GS保护机制并没有保护存放在栈上的 SEH 异常处理结构,因此,如果能够写入足够的数据来覆盖栈上的 SEH 记录,并在函数收场白和 Cookie 检测之前触发 SEH 异常,那么将会绕过Cookie的检查从而去执行我们构建好的异常处理例程。
安全结构化异常处理保护 Safe SEH
保护原理: GS保护缺陷就是可以通过覆盖SEH结构实现绕过,随后防守方就在其编译器中加入了Safe SEH保护措施,该选项需要在链接时添加 linker /safessh:yes
来开启,采用 SEH 句柄验证技术验证堆栈中SEH的合法性,来确保 SEH 结构不会被非法覆盖。
绕过措施: 为了突破 SefeSEH 的保护,攻击者又找到了新的绕过方式,通过利用进程中未被启用的 SEH 模块,将修改后的 SEH 例程指针指向这些模块中的 (POP/RET) 等一些跳板指令,从而跳转到栈上执行 ShellCode 代码,除此之外还可以将恶意代码布置到堆中,然后修改函数指针指向堆,同样可以绕过。
堆栈 SEH 覆盖保护 SEHOP
保护原理: 随后防守方进一步提出了 SEHOP 技术,该技术默认从 Windows Vista 开始支持,而该技术在Win7-Win8系统上默认是关闭的,你可以通过注册表开启该选项,该技术的核心原理是在程序运行时验证整个异常处理链表结构的完整性,如果攻击者覆盖了某个异常处理程序,那么该链表将被破坏,从而抛出异常停止执行。
绕过措施: 为了绕过SEHOP保护机制,突破方法就是进一步伪造 SEH 链,该方法的核心是能够找到合适的跳板指令,且伪造最终异常处理函数指针应该与真实的相同,伪造最终异常处理函数指针前4字节(SEH链指针)应为0xFFFFFFFF,SEH链指针地址应该能被4整除即可。
地址空间布局随机化保护 ASLR
保护原理: 如上所说我们需要找到合适的跳板指令,但恰巧的是防守方在此基础上又添加了一个新的技术,ASLR 地址空间布局随机化,该技术的核心原理是不让攻击者预测到布置在内存中的 ShellCode 的内存地址,因此即使溢出发生并成功填充内存,攻击者也无法得知将EIP指针跳转到何处,从而无法执行恶意代码。
绕过措施: 针对 ASLR 技术,攻击者同样的找到了能够绕过的方式,主要是利用堆喷射技术 (Heap Spray),通过使用脚本语言在堆上布置大量的含有 ShellCode 代码的指令块,从而增加某一个内存地址位于指令块中的命中率,通过执行概率实现挫败 ASLR技术。
堆栈数据执行保护 DEP
保护原理: DEP 保护直接切中了缓冲区溢出要害,数据执行保护将程序数据段所在的内存页面 (堆栈) 的属性强制设为 NX (不可执行),当程序执行这些内存页面上的数据时,将报错并禁止文件的执行,当今的CPU提供了硬件方面的安全防护,同样也支持了DEP保护技术。
绕过措施: 为了绕过DEP保护,攻击者提出了新的绕过方式 ROP(返回导向编程)
,它是ret2libc的继承者,攻击者在溢出程序之后,并不去执行栈中的 ShellCode 代码,而是寻找程序中已加载的特殊指令块,配合栈上的压栈参数,将这些相对孤立的指令串联起来,形成一条链,并通过调用 VirtualProtect函数
,将该栈设置为可执行属性,然后在执行栈中的 ShellCode 代码。
如今信息安全时代经历了三代:病毒时代,漏洞时代,对抗时代,而如今已进入了对抗时代,对抗不只是人与机器之间的对抗,更多的是人与人的对抗,随着 Windows 10 系统的普及,且 Windows 10 默认开启 DEP+ASLR 双向保护,在这样的环境下,想要绕过这些障碍,其难度可想而知。
最后总结: Web安全因为其简单,热门等特点,已成为了黑客们的下一个目标,而系统漏洞的挖掘已经变得非常困难了。
注:理解上方文字需要具有一定的汇编与系统原理相关知识。