刘 渊,谢家俊,张春瑞,严 可,滕 斌
(中国工程物理研究院 计算机应用研究所 新一代信息技术中心,四川 绵阳621900)
完整的漏洞挖掘包括异常发现和异常分析两部分。前者目的是发现软件潜在的安全隐患,后者目的是评估安全隐患的可利用性及威胁程度。
对于异常的发现,目前模糊测试是最有效的方法[1]。核心思想是构造海量的畸形样本,并监测程序处理过程是否发生异常。在高性能计算与程序分析技术的支撑下,模糊测试可自动化的发现大量的潜在安全隐患。例如文献[2-4]的工作通过搭建相应的平台,发现了各类软件数十个甚至上万个不同种类的异常,但是,这些异常是否可利用的软件漏洞仍需要大量的异常分析工作。异常分析存在着高度依赖人工经验、效率低下、经验难以复用等缺陷,已成为制约软件漏洞挖掘的瓶颈,导致大量安全隐患无法被正确认识、评估与消除[5],因此专业的异常分析人才已被认为是网络信息安全的核心[6]。可见,研究高效、通用性强的二进制程序异常分析方法具有重要意义。
为提高异常分析效率,近年来人们进行了深入的研究。微软的MSEC[7]是一款著名的二进制程序异常分析工具,可实现异常的分类及可利用性的初步评估。然而,由于软件异常形成原因的复杂性,多数异常被划分至 “未知”类别,无法提供可靠的分析信息。对于堆缓冲区溢出分析,Windows Debugger提供了 “页堆”机制,通过对ntdll.dll中的HeapAlloc函数申请的内存后添加不可读/写的栅栏页,实现对了堆缓冲区溢出的精确定位[7],可显著提高堆缓冲区异常的分析效率。然而,栈内存由应用程序自身维护,因此该方法无法分析栈缓冲区溢出产生的软件异常。Miller提出基于Bitblaze系统的差分污点标记分析方法,并列举了被MSEC判断为未知的5个异常的分析过程[5],但Bitblaze系统存在着稳定性差、运行速度极慢等严重缺陷,上述方法实用性较差;Cha等提出污点跟踪的软件异常分析方法,通过判断EIP是否被污点数据所控制实现了可覆盖保存在栈帧中EIP 的栈缓冲区溢出[9](下文均简称为“普通栈溢出”)的分析自动工作,并在此基础上实现了利用代码的自动生成方法[10,11],可见,针对普通栈缓冲区溢出的分析已较为成熟。
然而与普通栈缓冲区溢出不同,许多情况下程序只允许产生一个字节的栈缓冲区溢出,即单字节栈溢出 (通常称为off-by-one栈溢出)。单字节栈溢出的概念由Halvar Flake提出,并指出由于x86 采用小端 (little-endian)模式,溢出的这个字节只能覆盖保存在栈桢中的EBP的最低有效字节,无法覆盖保存在栈桢中的EIP 及S.E.H.等其它函数指针。单字节栈溢出发生时,程序不会在函数返回时崩溃,但运行逻辑会遭到严重破坏,进而引发深层安全隐患。实践结果表明,部分单字节栈溢出漏洞仍能允许攻击者实现任意代码的注入攻击,带来严重的信息安全风险。然而,据作者对公开文献的调研,目前单字节栈溢出漏洞的分析主要依赖手工调试,尚未发现自动化程度高、具备一定通用方法。针对上述问题,本文将提出一种单字节栈溢出异常的黑盒分析方法。该方法具有无需高层语义分析、自动化程度高、通用性强的特点,能显著提高单字节栈溢出的异常分析效率。
栈缓冲区动态的存储了函数之间的调用关系,以保证被调用函数在返回时恢复到母函数中继续执行。程序中的每一个函数都有自己独立的栈帧空间,当前正在运行的函数使用ESP寄存器指向栈顶,EBP寄存器指栈底。在函数栈帧中,一般包含局部变量、栈帧状态值、函数返回地址等重要信息,如图1所示。
图1 函数栈帧布局
当向缓冲区中存储数据时,如果没有正确检查数据拷贝的长度,将发生缓冲区的溢出,覆盖保存在栈中的函数返回地址ret、S.E.H.,导致在函数返回 (ret)或调用异常处理函数时控制流被改变。若攻击者精心构造输入样本,可将程序控制流劫持至攻击者可控制的地址 (如buffer),实现欺骗程序执行任意代码的目的。
普通栈溢出可直接覆盖ret或S.E.H.等重要函数指针,实现控制流被输入劫持。因此,在不考虑如ASLR (地址空间布局随机化)、Cookie、DEP (数据执行保护)等内存保护的情况下,普通栈溢出通常是可利用的,异常分析与漏洞验证有相对固定的方法可循,主要分析思想如图2所示。
图2 普通栈溢出异常的分析思路
典型的分析步骤如下所示:
(1)在调试器监控下让程序处理异常样本,收集异常信息。
(2)根据异常信息定位样本中覆盖EIP 的输入字段,通常可通过在样本中直接搜索得到。例如调试器反馈EIP非法访问了0xAABBCCDD 地址的内存,可直接通过在样本中搜索DDCCBBAA (注意x86 系统采用小端模式)实现。
(3)修改相应的字段至JMP ESP 的地址。例如0x7FFE4512是Windows XP 中文版各不同SP 版本相对稳定的地址,因此可将样本中的DDCCBBAA 修改为1245EF7F即可实现对EIP 的劫持至栈顶指针ESP 指向的内存 (通常为可被输入覆盖的内存)。
(4)根据异常点内存布局,在输入样本中相应的位置布置shellcode。
(5)完成攻击样本的编写。
对于特殊情况,如输入被截断、处理后无法直接在样本中搜索到异常地址、异常点ESP 后的内存无法被覆盖等情况,需要进一步调试分析。有兴趣的读者可参看相关书籍,本文不再赘述。
可见,异常分析的核心是定位输入样本中可控制EIP的字段。在发现输入样本中控制EIP 的字段后,异常的分析便有较固定的方法可循。在下文的单字节栈溢出异常的分析中,将利用上述结论简化分析过程。
单字节栈溢出漏洞是由内存拷贝时边界控制错误导致,如图3所示的代码片断。
图3 单字节溢出代码片断
虽然函数试图防止字符串复制时的数组越界,但C 语言数据从0开始的约定容易让习惯于其它编程语言的程序员在数组边界位置出错。例如上述循环控制中语句应为“i<len”,而程序员误写为 “i<=len”,导致了单字节栈溢出的产生。当缓冲区后正好紧跟着EBP和EIP时,溢出数组的一个字节将覆盖EBP的最低有效字节,导致在函数返回后攻击者能在255个字节范围内移动EBP寄存器。
溢出不会立即导致程序终止,但将破坏程序正常执行逻辑,进而导致异常的产生。为便于说明,假设EBP 的最低有效字节为0xC0 被我们覆盖为0x00。当函数返回时,将执行恢复栈帧的操作,即将分别执行mov esp,ebp;pop ebp;ret,栈的内存变化如图4所示。
可见,在函数返回后,仅栈底指针寄存器EBP 被移动至0x0012FF00,而ESP、EIP等寄存器值正常。因此,程序通常可以正常执行。
然而,当程序运行至下一次函数调用时,将执行mov esp,ebp及pop ebp等保存当前运行时状态的操作,如图5所示。
图4 函数第一次返回时栈内存变化
图5 单字节栈溢出漏洞形成原理
可见,在执行mov esp,ebp操作时,由于EBP可被攻击者在255字节范围内移动,因此,该操作将进一步导致攻击者可在255字节范围内移动ESP。接下来,程序运行pop ebp时,将使得错误的ESP 指向的值被弹至EBP,通常将导致EBP为畸形值。因此,可根据该特点判断异常是否为单字节栈溢出导致。接下来,由于ESP的错位,导致程序运行逻辑发生严重错误,因此程序将因异常而终止。
可见,单字节栈溢出异常的分析面临着如下难点:
(1)难以定位溢出字节。对于栈缓冲区溢出的精确定位,需要分析缓冲区大小及起始位置,并在内在拷贝时进行相应的检测。然而二进制程序本身并没有缓冲区的概念,缓冲区及其大小由程序高层语义赋予,在没有源代码支持的情况下,无法实现精确的静态指针分析[12],因此难以有效定位溢出字节。
(2)将导致程序产生不同的异常。触发单字节栈溢出的函数会正常执行,仅仅是在返回时EBP最低有效字节被改变,在程序的下一次函数调用过程中导致程序的控制逻辑受到破坏,进而导致程序的执行异常。从溢出至程序崩溃,已经运行了大量指令。同一个单字节溢出情况,采用不同的值覆盖EBP最低有效字节将导致程序在不同的地址崩溃,增加了手工调试分析的难度。
虽然上述问题难以克服,但本文认为对单字节栈溢出异常分析任务,上述问题通常是可绕过的。
(1)单字节栈溢出异常是可识别的。虽然单字节栈溢出可能导致不同的程序异常,但在异常发生前,会将栈中某个值POP至EBP,因此,可根据畸形的EBP值判断漏洞是否为单字节栈溢出。 (虽然普通栈溢出也将导致EBP 畸形,但此时表现为EBP、EIP 均被输入控制,可根据1.2节方法分析和排除)。
(2)异常可利用性的判断可以无需精确定位溢出点。控制劫持流的本质就是利用输入控制EIP,并不需要回溯、定位到导致缓冲区溢出的指令。因此,若在程序崩溃处能发现输入可以控制EIP,将大大简化异常分析工作。
(3)无需分析所有异常,只需实现一个异常的利用便可完成威胁的评估。从上述分析可知,栈帧的抬高 (即ESP减小)更有利于形成可利用的环境。从2.2节的分析中,我们知道单字节栈溢出将允许攻击者通过覆盖保存在栈中的EBP进而在一定范围内移动ESP。由于栈具有从高位向低位生长的特性,抬高栈更有利于ESP 指向应废弃的、但被用户输入控制的缓冲区中。
综上所示,本文提出了基于黑盒测试思想的单字节栈溢出分析方法,实现过程如图6所示。
该方法主要分为以下几个步骤:
(1)单字节异常的确定。2.2节的分析结果表明,单字节异常将导致EBP 异常且EIP 正常 (即程序可正常执行)的情况,在程序运行过程中的EBP及EIP能确定异常是为由单字节栈溢出导致。具体做法可采用手工调试或指令追踪等方式实现。
(2)异常点EIP被是否被输入控制的监测。可利用在Intel pin的基础上利用污点分析技术实现。然而,污点分析将大大降低测试速度。因此,本文在Windbg输出的异常信息,通过在样本里搜索是否包含EIP 的值实现EIP 是否被输入控制的监测。
图6 单字节栈溢出的黑盒分析方法
(3)样本变异。随机的对样本的多个字节或单个字节进行变异。为实现栈帧的抬高以形成更利于漏洞利用的环境,更高的机率修改为较低的值。由于0x00通常被判定为字符串结尾,因此本文以更高的概率将样本修改为0x01~0x41之间的值。
(4)可利用性的判定。对于EIP 被输入控制的样本,可初步判定为可利用异常。若通过测试超过了预设时间或次数而并未发现可以通过输入控制EIP,便可初步判定该单字节异常是可能是不可利用的。
(5)对可利用异常的验证。可参考1.2 节普通栈溢出的方法实现可利用性的验证。但与普通栈溢出不同,在单字节栈溢出中实践中JMP ESP后攻击者可利用的缓冲区通常较少,需要更加深入的分析异常点内存布局并编写相应的跳转代码。
为验证方法的有效性,本文选择了自己编写的存在单字节漏洞的软件及存在Winamp 5.12与Apache 2.0.58两款存在单字节漏洞的真实软件上进行。实验及结果见表1。
表1 单字节栈溢出黑盒分析方法的验证实验
从表1可知,在仅获得一个异常样本的情况下,通过本文方法的不断变异,可找EIP 被输入控制的样本,进而在此基础上可实现利用代码的编写和漏洞的验证。实验结果表明,该方法无需深入分析程序的内部逻辑,因此具有黑盒测试的易于部署、通用性强等特点。样本变异的本质是在单字节栈溢出可能导致的众多异常中,寻找一个更利于分析的异常,将异常可利用性验证转化为与经典栈溢出类似的有相对固定的方法及步骤可循的工作,因此显著提高单字节栈溢出异常分析效率。
本文深入分析了单字节栈溢出异常的形成原因及异常分析的难点,提出了一种单字节异常的黑盒分析方法。该方法无需深入分析程序内部语义,实现简单,能显著提升单字节漏洞的分析与利用的效率。该方法在单字节异常的确认及利用代码生成上仍需少量人工参与,无法实现完全自动化。未来工作将借助指令追踪技术,通过Intel Pin工具在程序执行过程中插装相应的检测代码,实现异常类型的自动判断并定位输入控制EIP的字段,进而实现单字节栈溢出异常的全自动分析。
[1]WU Zhiyong,WANG Hongchuan,SUN Lechang,et al.Survey of fuzzing [J].Application Research of Computers,2010,27 (3):829-832 (in Chinese). [吴志勇,王红川,孙乐昌,等.Fuzzing技术综述 [J].计算机应用研究,2010,27 (3):829-832.]
[2]Nagy B.Finding Microsoft vulnerabilities by fuzzing binary files with ruby-a new fuzzing framework [C]//SyScan.http://www.syscan.org/Sg/program.html,2009.
[3]Miller C.Babysitting an army of monkeys:An analysis of fuzzing 4 products with 5lines of Python[C]//Cansecwest,2010.
[4]Avgerinos T,Rebert A,Cha S K,et al.Enhancing symbolic execution with veritesting[C]//Proc ICSE,2014:1083-1094.
[5]Miller C,Caballero J,Johnson N M,et al.Crash analysis with BitBlaze[C]//BlackHat USA,2010.
[6]Miller C.Kim Jong il and me how to build a cyber army to attck U.S.[C]//Defcon-18,2010.
[7]!exploitable crash analyzer [EB/OL].http://msecdbg.codeplex.com/,2013.
[8]Soulami T.Inside windows debugging [M].Pearson Education,2012.
[9]Cha S K,Avgerinos T,Rebert A,et al.Unleashing mayhem on binary code[C]//IEEE Symposium on Security and Privacy,2012:380-394.
[10]Avgerinos T,Cha S K,Hao B L T,et al.AEG:Automatic exploit generation [C]//Proceedings of the Network and Distributed System Security Symposium,2011:59-66.
[11]Caselden D,Bazhanyuk A,Payer M,et al.Transformationaware exploit generation using a HI-CFG [R].California Univ Berkeley Dept of Electrical Engineering and Computer Science,2013.
[12]Szekeres L,Payer M,Wei T,et al.SoK:Eternal war in memory [C]//IEEE Symposium on Security and Privacy,2013:48-62.