用户与内核空间的控制流完整性方法回顾外文翻译资料

 2023-03-15 04:03

用户与内核空间的控制流完整性方法回顾

摘要

攻击者已将经典的代码注入攻击(如由缓冲区溢出引起的攻击)演化为复杂的图灵完全代码重用攻击。控制流完整性(CFI)是一种防御机制,用于消除由常见内存错误引起的控制流劫持攻击。CFI依赖于静态分析来创建程序的控制流图(CFG),然后在运行时CFI确保程序遵循合法的路径。因此,当攻击者试图执行恶意外壳代码时,CFI会检测到意外路径并中止执行。CFI在很大程度上依赖于静态分析来精确生成控制流图,其安全性取决于CFG生成和执行的严格程度。

本文回顾了近十年来提出的CFI方案,并对其针对先进开发技术的安全保障进行了评估。

关键词:控制流完整性,代码重用攻击,操作系统安全

1简介

操作系统必须确保其自身的代码和应用程序的代码在攻击者的威胁下安全与可靠。由于代码注入攻击是众所周知的,现在的攻击者通常利用内存损坏漏洞来破坏操作系统或正在执行的应用程序的控制流。现代防御系统不是以保护代码完整性、完全内存安全或开发C/C 安全方言为重点,而是试图保护这些系统的控制流完整性。

自从引入CFI[1]以避免这些问题和问题以来,社区提出了不同的实现和版本,试图使其切实可行,同时确保其保护的完整性。此外,还提出了限制这些方法有效性的新攻击。由于CFI方法对系统安全社区的相关性越来越高,本文首先对控制流完整性防御和试图颠覆它们的攻击进行了全面的文献综述和讨论。

2控制流完整性

C/C 代码与内存损坏缺陷密切相关,这使得对手可以利用这些内存错误发起攻击。由基于堆栈或基于堆的溢出、悬空指针/UAF和格式字符串漏洞引起的代码注入攻击很常见,可以使用write xor execute(Woplus;E) /Data Execution Prevention(DEP)[4]和stack canaris[16]进行防护,这些防护机制都包含在当今的编译器和操作系统中。然而,代码重用攻击,如returninto libc[36]、returnoriented programming(ROP)[44]和jump-oriented programming(JOP)[5,12]仍然不能完全防护。操作系统本身也不能免受代码重用攻击,例如return to user(ret2usr)[33],return to libc的内核级变体,以及sigreurn-oriented programming(SROP)[6],它利用类UNIX系统的信号处理能力,以与ROP和JOP分别处理ret和jmp指令相同的方式部署gadget。

操作系统部署统计防御来保护用户空间和内核空间免受代码重用攻击;即地址空间布局随机化(ASLR)[47]和内核ASLR[22]。然而,由于信息泄漏和针对用户[45]和内核[29]空间的即时代码重用攻击,这些防御措施可以被规避。

考虑到这些问题,Abadi等人引入了控制流完整性(controlflowintegrity,CFI)[1],这是一种防止试图破坏程序的合法执行流的代码重用攻击的防御机制。

CFI的工作分为两个阶段:首先,通过源程序或二进制代码静态分析计算程序的控制流图(CFG);之后,在程序执行期间,CFI强制程序遵循合法的执行路径;否则程序中止。

在计算阶段,CFI涉及到分析点,即处理指针可能值的静态分析,因为它影响生成CFG的精度[8],从而影响强制执行阶段强制执行合法执行路径的精度。考虑到精确度,CFI实现可以分为(i)流敏感或流不敏感和(ii)上下文敏感或上下文不敏感。一方面,流敏感算法使用程序的控制流信息来确定指针的可能值,而流不敏感算法则计算一组对所有程序输入有效的值[25,26]。另一方面,上下文敏感算法在分析函数时考虑上下文,防止值传播到不可行的路径,从而保证调用的上下文与其他调用上下文保持独立;相反,上下文不敏感算法允许函数返回到所有调用者的计算集[26,52]。

在强制执行阶段,CFI可以考虑向前(例如间接调用或跳转)和向后(例如返回指令)控制流传输。只提供控制流传输的前向强制的CFI解决方案已经被发现是不安全的[10,21,23],而强制后向传输的解决方案通常依赖于一个影子堆栈,影子堆栈是一个保存控制流副本的结构返回存在于原始堆栈的每个堆栈帧中的地址,维持影子堆栈会导致程序开销增加10%[19],强制后向传输的解决方案也会使用最后一个分支记录寄存器(LBR)[3,31],但是这些寄存器只能用于CPU的一个子集,并且存储容量有限。

CFI的绝大多数实现都是为了保护用户空间,并且有编译器扩展、源代码或二进制代码修补框架和内核模块的风格,而有一小部分是为了保护部署内核修改或新内核模块的操作系统。

2.1用户空间实现

最初的CFI[1]通过重写机器代码对x86二进制文件进行操作。对于前向控制流传输,重写过程包括在每个目的地插入ID,以及在每个源之前进行ID检查;然后在运行时,源ID和目标ID必须一致。为了确保函数调用返回到适当的调用站点(向后控制流传输),该实现使用依赖于x86分段功能的影子调用堆栈。CFI要求(i) 不可写的代码,以防止攻击者重写ID检查;(ii)不可执行的数据,以防止攻击者执行使用预期ID生成的数据。第一个要求在现代操作系统中是正确的,不包括动态库的加载时间和运行时代码生成,第二个要求是Woplus;E这种实现假设如果两个目的地是从同一个源调用的,那么它们是等价的,从而在CFG中引入了不精确性,从而在实施阶段引入了不精确性。

MoCFI[20]为运行在ARM处理器上的iOS设备应用程序提供CFI保护。它解决了ARM体系结构的特殊问题(例如,不存在专用的返回指令)。作为最初的CFI,它也对二进制文件进行操作。作者生成应用程序的CFG和包含应用程序中间接分支和函数调用的元数据的补丁文件;应用程序中使用的动态库不受保护。在运行时强制阶段,补丁文件由MoCFI共享库使用,生成在CFI策略内执行的补丁应用程序。MoCFI使用影子堆栈来保护调用和返回。然而,对于前向控制流传输,它不能保护在静态分析中无法识别目的地的间接跳转/调用;因此,它们可以针对函数中的任何有效地址,或者分别针对任何有效函数。

与MoCFI不同的是,CCFIR[55]和Bin CFI[56]是另外两个二进制实现,包括对库的保护。一方面,CCFIR使用windowsx86pe可执行文件,部分支持库。它建立在Abadi等人的方法之上,并结合了第三个新的ID检查,用于返回敏感和非敏感函数。这种实现也有同样的Abadi等人提出的前边缘的不精确性,以及后边缘的不精确性的问题。另一方面,Bin CFI保护剥离的Linux x86二进制文件,包括共享库。这种方法类似于原来的CFI方案,并且具有比CCFIR更低的安全保证。最近的研究发现,由于对目的地进行分组,Bin CFI和CCFIR的保护都不够[21,23]转换为等价类的能力不足以阻止它们被用作ROP/JOP小工具。

kBouncer[40]是一个基于硬件的Windows工具包,它依赖于Intel Nehalem体系结构的LBR寄存器来检索关键点(如系统调用)的最新16条间接分支指令序列。kBouncer总共保护52个Windows API函数的执行。与kBouncer类似,ROPecker[14]是一个使用LBR的Linux x86内核模块注册以防止代码重用攻击。这两种方案都依赖于链长度和gadget长度启发式算法来防止此类攻击。然而,通过选择合适大小的gadget链长度[10,21,24],可以绕过它们。

最近一个基于二进制的x86/64 CFI实现O-CFI[35]将代码随机化与CFI检查结合起来。O-CFI首先计算每个间接分支的允许目的地地址,然后将间接分支必须到达有效目的地的策略转化为边界检查问题;因此,O-CFI必须检查目标地址是否存在于最小/最大地址边界。这些边界使用代码随机化进行保护,然后使用Intel的内存保护扩展(MPX)[30]进行检查。O-CFI使用了一种前向和后向控制流传输检查的放松版本,因此可以绕过。

所有先前提出的在二进制代码层面的保护方法[1,20,35,40,55,56]都不能获得完全的上下文敏感性;然而,由于使用了阴影堆栈,其中一些支持部分(向后)上下文敏感度[1,20]。PathArmor[49]是第一个解决前向和后向边缘上下文敏感问题的二进制保护方案。上下文敏感的CFI方法需要跟踪已执行的控制流传输的路径,以便稍后强制执行遵循合法路径。PathArmor没有使用影子堆栈,而是使用LBR寄存器来模拟受LBR寄存器数量限制的路径监视机制(仅16个)。PathArmor在前向边缘传输方面优于所有以前的保护方案。然而,由于当前硬件的限制,基于阴影堆栈的方法仍然是更可靠的向后边缘跳转保护方法。

关于依赖源代码的CFI实现,Tice等人[48]提出了两种不同的前向边缘保护机制,分别集成在生产编译器中,即GCC和LLVM的虚拟表验证(VTV)和间接函数调用检查(IFCC)。基于堆栈的攻击已被发现有效绕过VTV/IFCC[15],随后的编译器已修补。SafeDispatch [32]是较早的LVVM编译器扩展,和VTV一样,旨在保护C 虚拟调用的虚拟表(VTABLE);VTV和SafeDispatch都不能提供完全的控制流保护,因为它们只关注前沿。为了保护vtables,我们做了进一步的研究,最终得到了两个二进制级别的实现,VfGuard[42]和VTint[54];但是它们也仅限于部分控制流保护。

控制流重用攻击的最新形式:伪造面向对象编程(COOP)[43]可以使用C 虚拟功能的小工具来安装图灵完整攻击。COOP对原始CFI、bin CFI、CCFIR、VTint有效,部分对IFCC、VfGuard和PathArmor有效。相反,TypeArmor[50]可以在二进制级别防止COOP,而在源代码级别可以通过编译器扩展SafeDispatch、VTV、VTrust[53]和VTI[7]防止COOP。

Niu和Tan介绍了模块化CFI(MCFI)[37],这是一种通过模块化编译扩展CFI的新方案。在MCFI的基础上,作者提出了RockJIT[38],它在即时编译器中实施CFI;MCFI和RockJIT都在边缘生成中引入了一些不精确性,因为它们对等效目标应用了与原始CFI相同的假设。相反,pi;CFI(per input CFI)[39]为基于源代码的CFI解决方案引入了最高的安全保证。pi;CFI在处理CFG生成的方式上不同于所有以前的CFI实现。保守的CFI实现在执行阶段之前使用静态分析来计算CFG,这种分析被认为是困难的,因为它必须考虑给定程序的所有可能输入值;此外,CFI的安全保证严格受限于CFG的精度。Niu和Tan指出,即使一个完美的CFG是可能的,但对于给定的输入,它仍然会包含不必要的边。因此,他们通过以下方式解决CFG生成问题;首先,它们为所有程序输入生成保守的CFG(基于MCFI和RockJIT),然后在程序执行期间,给定一个输入,pi;CFI动态生成CFG边,但只执行那些符合保守的所有输入 的CFG的边。与阴影堆栈方法相比,这种创新方案提供的后向跳转保护更少,但更高的后向跳转保护比其他后向跳转保护方法更有效。关于前向跳转,pi;CFI有更强的保

剩余内容已隐藏,支付完成后下载完整资料


英语原文共 10 页,剩余内容已隐藏,支付完成后下载完整资料


资料编号:[595987],资料为PDF文档或Word文档,PDF文档可免费转换为Word

您需要先支付 30元 才能查看全部内容!立即支付

课题毕业论文、开题报告、任务书、外文翻译、程序设计、图纸设计等资料可联系客服协助查找。