为提升静态分析在二进制文件漏洞检测领域效率和可扩展性,科恩孵化并开源二进制文件静态漏洞分析工具BinAbsInspector项目。https://github.com/KeenSecurityLab/BinAbsInspector随着信息产业的发展,网络安全问题日益严峻,软件漏洞对于互联网威胁极大,是网络安全中的核心问题。为了缓解漏洞所造成的危害,需要对软件进行安全检测,尽可能地发现并消除潜在漏洞。目前常见的自动化漏洞检测手段可以分为两类:动态分析测试和静态分析。动态分析测试方法(如fuzzing等)在过去五年里吸引了研究者的广泛关注,相关系统在工业界中已经得到了大规模的部署和应用。相比于动态方法,静态分析通常能够得到更为全面可靠的程序状态,可以从根源上有效地克服动态分析的覆盖率问题;然而在现实中静态分析如何兼顾平衡精度和复杂度在学术界和工业界仍是一大难题,在产品中也多依赖人工经验规则进行补充,这导致其在现实场景中的落地不尽如人意。
目前国际上较为成功的商业化分析工具有Coverity[1]、CodeSonar[2]、VeraCode[3]等,它们在代码质量保障上发挥了重要作用,相关产品也在Google等公司的DevOps流程中得到了广泛部署和使用。
包括开源及商业化产品在内,现有的静态分析方案多为源码级分析。面向源代码进行扫描,尽管可以在一定程度上满足软件安全需要,然而在真实安全场景中,待分析对象多为二进制文件,如嵌入式系统固件,商业软件等,研究人员难以获得相应的源代码,此时源码级静态分析方案不再适用。值得一提的是,部分商业化产品(如CodeSonar等)也提供了对于二进制文件的分析能力,然而商业化路线所带来的封闭性,在很大程度上限制了普通研究者的使用和二次开发。与此同时,在开源社区中也涌现出一批知名的二进制分析工具,如angr[4]、BAP[5]、cwe_checker[6]。其中,angr和BAP逐渐往通用分析框架发展,并非专注于二进制漏洞扫描,因此其内部的分析算法较为庞杂,不利于进一步扩展和优化;cwe_checker的定位相对清晰,专注于安全漏洞扫描,但其精度和效率却不甚理想。目前业界亟需一种更为先进的二进制漏洞扫描工具,在开源的大前提下,其性能和可扩展性也要满足真实场景的需要。为此,科恩实验室基于自身在二进制领域丰富的研究与实践经验,同时结合业内相关优秀工具的设计理念,最终孵化出性能出色且自主可控的二进制漏洞静态扫描工具——BinAbsInspector。BinAbsInspector的设计思想来源于上世纪70年代诞生的经典程序分析理论“抽象解释”;在具体实现上,BinAbsInspector的分析基于Ghidra[7]所提供的中间表示Pcode上。通过设计合适的抽象域,实现其上的多种运算,完成相关Pcode的操作语义,执行流敏感(flow-sensitive)和上下文敏感(context-sensitive)的过程间分析,同时加入静态污点分析的能力,完成对程序运行时状态的抽象估计,最后基于上述分析所得的抽象数据流信息对多种漏洞类型进行建模检查,实现对二进制漏洞的静态扫描。
关于如何对程序运行时状态进行抽象这一关键问题,我们主要参考了经典论文《WYSINWYX: What you see is not what you eXecute》[8]中的做法并加以改良、简化和提升。具体来说,在BinAbsInspector中整个运行时环境被分为Local (抽象栈)、Heap(抽象堆)、Global(全局变量和数值)、Unique(对应Ghidra中产生的临时变量区)和Register(寄存器区)五种region。在这些不同的抽象区域上加上偏移数值offset,便可以组成一个抽象变量ALoc(Abstract Location/Variable)。因为在二进制程序中,变量并非全部显式表示,ALoc便是对实际程序中变量的一种估算和识别。对应不同的程序点,需要记录此处可能存活的抽象变量和其对应的抽象值,称之为AbsEnv(Abstract Environment)。因为是静态的抽象,那么对于一个程序点的一个抽象变量,它可能会包含多个抽象值,这些抽象值组成了一个集合。虽然这个集合可能会包含无穷多个元素,但是为了保证整个计算过程实践上可收敛,令此集合取一个上限K,这种集合称之为KSet。一旦其中包含的元素超过K,便将其变为一个Top,即包含所有抽象值。此方法与前人重要相关工作Jakstab[9]中的KSet较为相似。KSet支持多种算数和逻辑运算。此外每一个KSet对象也会包含一个污点的位图,用来跟踪多个污点的同时传播,从而实现静态污点分析。这样AbsEnv便可以认为是一个从ALoc到KSet的map。由于BinAbsInspector的分析是上下文敏感的,对于被调用者的上下文 (Context),我们使用最近的call string(call site)来进行唯一标识。即对于同一个被调用者,不同的调用者会生成不同的Context,一般只记录最近的几个调用者。这样我们便把程序点处的AbsEnv记录在不同的Context中。除此,对于过程内的不动点计算BinAbsInspector里使用了worklist算法,即把待处理的程序点不断地放入worklist中,直到其空为止。过程间分析主要在于不同的Context之间的转变,这是通过call/return指令的语义实现的。这样通过对整个程序指令的迭代计算值并加以Context的转换,Context及其附属的worklist得到逐一处理,直到所有的worklist计算结束,最后达到不动点。通过这整个的计算过程,便会得到所有可能的Context以及对应的每个程序点的AbsEnv。这样相当于得到了一个对程序行为可靠的估算,有了这些抽象数据流的信息,我们便可以进行内存破坏漏洞、命令注入漏洞等多种漏洞的检测了。下面我们通过一个包含Use-After-Free漏洞的简单样例来演示BinAbsInepector的运行情况和基本原理。“CWE416_Use_After_Free__malloc_free_struct_01_bad”函数中首先调用“malloc”函数分配内存用于存放100个“twoIntsStruct”对象—>依次对这部分对象进行初始化操作—>直接释放内存—>释放内存过后再次调用了“printStructLine”函数访问已释放内存中的地址—>造成Use-After-Free漏洞。
void CWE416_Use_After_Free__malloc_free_struct_01_bad()
{
twoIntsStruct * data;
/* Initialize data */
data = NULL;
data = (twoIntsStruct *)malloc(100*sizeof(twoIntsStruct));
if (data == NULL) {exit(-1);}
{
size_t i;
for(i = 0; i < 100; i++)
{
data[i].intOne = 1;
data[i].intTwo = 2;
}
}
/* POTENTIAL FLAW: Free data in the source - the bad sink attempts to use data */
free(data);
/* POTENTIAL FLAW: Use of data that may have been freed */
printStructLine(&data[0]);
/* POTENTIAL INCIDENTAL - Possible memory leak here if data was not freed */
}
int main(int argc, char * argv[])
{
/* seed randomness */
srand( (unsigned)time(NULL) );
printLine("Calling bad()...");
CWE416_Use_After_Free__malloc_free_struct_01_bad();
printLine("Finished bad()");
return 0;
}
BinAbsInspector作为Ghidra Extension的形式进行开发,构建后安装在Ghidra中,支持GUI和Headless模式运行,用户也可以通过项目中提供的Dockerfile构建docker镜像体验功能,具体使用方法见【仓库README】:https://github.com/KeenSecurityLab/BinAbsInspector。在此我们以GUI模式为例介绍使用步骤。
首先将BinAbsInspector安装在Ghidra,我们将编译好的样本程序(armv7)导入Ghidra,待Ghidra的分析完成,便可以运行我们的分析了。这时会弹出工具的分析选项框,将分析过程的配置参数暂时保持默认即可。分析显示找到2处CWE告警,在下方命令行中会显示具体告警的地址。
标记1:触发告警的地址,即产生Use-After-Free访问的指令地址;图2-1 分析结果展现
双击命令行中的告警地址可以在汇编窗口跳转到对应的指令,另外右边的反编译窗口也将同步展示对应的伪代码,可以看到两条告警的指令都位于“printStructLine” 函数的内部,都是访问已释放内存的LDR指令,结果符合预期。
图3-1 分析溯源反汇编框
图3-2 分析溯源反编译框
原理上简而言之,在第6行“malloc”处创建了一个Heap region,data的抽象值为此Heap及偏移值0,这一信息被加入当前的AbsEnv中并继续向后传播,经过第17行的“free”,data的抽象值数值保持不变,其中的Heap region变成了一个相同数值但是为无效状态的新region,当前的AbsEnv也会同步更新这一变化并将这一改动向后传播,最后在19行“printStructLine”内部的LDR指令时,通过查询传播到此的AbsEnv,检测到data指向的是一个无效的Heap region,这样便可以查找出这个Use-After-Free的问题。
我们选取Juliet[10]这一较为权威的测试集,在x86、x64、armv7三个架构上进行测试,并与cwe_checker测试结果进行对照比较。另外,BinAbsInspector也支持对aarch64架构样本的检测,这是cwe_checker目前不支持的。
下面表格展示的是在CWE415 (Double-Free), CWE416 (Use-After-Free),CWE476 (Null Poionter Deference), CWE78 (Command Injection) 4种核心漏洞检测能力与cwe_checker的对比结果。结果表明,BinAbsInspector的测试结果在所支持的CWE类型上均取得较大幅度优势。(BAI: BinAbsInspector, CC: cwe_checker, TP: True Positive正阳性, FP: False Positive假阳性, TN: True Negative正阴性, FN: False Negative假阴性)科恩在二进制安全研究领域有着深厚积累,其中二进制软件成分分析平台BinaryAI[11]已免费开放,推动软件成分分析在DevSecOps、安全研究等场景的应用与发展。在二进制静态分析方向,科恩孵化高效、准确的自动化二进制文件静态漏洞分析工具BinAbsInspector。经过内部应用实践及优化,BinAbsInspector已达到了较好的完成度。
BinAbsInspector未来将持续打磨算法及工程上的可提高之处,结合科恩二进制分析、算法等能力输出,赋能更多软件相关从业者,助力提升整体代码安全防治效率。关于更多的技术细节和代码实现,请移步我们的开源仓库及wiki文档,点击文末“阅读原文”即可跳转。欢迎所有感兴趣的小伙伴一起参与协同开发,在实践中迭代优化,打造更优秀的二进制漏洞静态分析利器。[1]https://www.synopsys.com/software-integrity/security-testing/static-analysis-sast.html[2]https://www.grammatech.com/products/source-code-analysis[3]https://www.veracode.com/[5]https://github.com/BinaryAnalysisPlatform/bap/[6]https://github.com/fkie-cad/cwe_checker/[7] https://ghidra-sre.org/[8]https://dl.acm.org/doi/pdf/10.1145/1749608.1749612[9]http://www.jakstab.org/[10]https://samate.nist.gov/SRD/testsuite.php[11]https://www.binaryai.net/
文章来源: https://mp.weixin.qq.com/s?__biz=MzU1MjgwNzc4Ng==&mid=2247503848&idx=2&sn=f6e95647f374658bd85a7c9014f1f0ad&chksm=fbfef3edcc897afb6ee849a395375b83bc4f2e3129e9d787e3011ce5c9e09627d23bad0e2d04&scene=58&subscene=0#rd
如有侵权请联系:admin#unsafe.sh