随着近年来云计算的不断发展和大规模应用,越来越多的重要业务系统被部署到云平台上以提高硬件资源利用率和降低运营成本。面向返回编程(return oriented programming, ROP)作为一种高级的代码重用攻击技术,能够绕过数据执行保护等防御机制,近年来更是逐渐发展出long gadgets[1]、call-preceded gadgets[2-3]、JOP(jump oriarted programming)[4]、Just-in-time ROP[5]等多样化变种,具有极强的破坏性和极高的灵活性,已成为常见的攻击手段之一。云计算提供商作为服务的发布者和运行平台的提供者,有必要具备实时检测客户虚拟机上ROP攻击的能力。
针对单机下的ROP防护,目前研究者们按照增加指令地址不可知性、检测gadgets链特征、保障程序控制流完整性(control flow integrity, CFI)等思路提出了一系列有效的检测机制。这些检测防护方法往往需要被攻击机修改操作系统内核[6]、编译器[7],或者对被保护程序进行静态分析和二进制重写[8]作为配合,部署复杂,开销较高且透明性差,难以满足云平台环境下的检测需求。在真实的云计算应用环境中,客户机系统具有大规模托管、部署与迁移属性,要求ROP的检测方案既能保持对客户虚拟机的完全透明,同时又能够灵活、可定制的快速部署。
随着虚拟化的快速演进,基于虚拟机监控器(hypervisor)的检测方案逐渐被提出以抵御虚拟机内用户层或内核层的代码复用攻击。HyperCropⅡ[9]利用虚拟机监视器的高级权限中断用户虚拟机进程中函数栈帧的写入操作,检查当前一段函数栈中是否超过阈值,但判别规则较为简单,限制了检测的准确性。SIGROP[10]在KVM平台上实现了基于硬件计数器的虚拟机ROP攻击检测,但仅使用硬件计数器进行数值分析的方法无法获得虚拟机内的高层语义信息,也无法定位到指令级的异常地址,检测率较低。
最后分支记录器(last branch record, LBR)是Intel提供的一组记录CPU上最近的32条跳转指令源地址和目的地址的循环寄存器栈,本文提出了一种基于LBR的虚拟机用户层ROP攻击检测方法。该方法易于实施,方便部署,能根据资源情况和检查粒度按需调度,并且不需要客户虚拟机的参与配合,具有良好的透明性的同时减小了虚拟机自省(virtual machine introspection, VMI)分析内存规模的量级,使得整个检测流程实时高效。
1 方案设计与实现 1.1 整体架构本文以Xen虚拟化架构为例,假设hypervisor安全可信,阐述客户虚拟机DomU用户层ROP攻击检测方案的整体架构。如图 1所示,通过在hypervisor中事先设定非常规的快速系统调用号,使得DomU在执行系统调用、进入内核态的时刻开启检测流程。根据LBR中获取的间接分支跳转信息,本文在hypervisor层进行快速的gadgets链检测,若检出攻击则直接发送虚拟中断通知Dom0的报警模块,否则将LBR信息、虚拟机ID、时戳TSC连同CR3寄存器中存放进程页表基址记录到内存缓存区中,然后经由VM-entry恢复DomU的执行。
为保证shellcode的健壮与稳定,攻击者通常从共享链接库中搜寻gadgets构造ROP或JOP链,且攻击链都不会很长。另外应用程序中的系统调用一般封装在libc等共享链接库中。因此,本文在特权域Dom0中设计了针对共享库中分支跳转的CFI检查模块。具体来说,Dom0取得记录数据后,首先按虚拟机ID对数据进行分离重组,借助VMI技术获取DomU进程描述结构体(task_struct)中的虚拟内存空间(virtual memory areas, VMA)信息,准确定位共享链接库的映射基址。将LBR记录的跳转指令地址转换成在对应链接库中的偏移,在离线收集的合法分支跳转数据库中进行逐条比对,从而保证库中跳转的控制流完整性。整个检测过程既不需要修改DomU的内核、编译器,也不需要DomU主动配合传递数据,对所监控的虚拟机完全透明。
1.2 LBR过滤与检测触发LBR由IA32_DEBUGCTL寄存器的最低位控制开启。然而,如果时刻不停地记录虚拟机的所有跳转,产生的信息体量无疑相当大且冗余度非常高,严重影响检测性能。由于攻击者在劫持控制流后要作出进一步恶意的操控和破坏,必将进行关键的系统函数调用。因此,本文只记录DomU关键系统调用之前的间接分支跳转,在保持较高检测有效性的同时降低开销。
Intel提供64位的MBR_LBR_SELECT寄存器用于设置分支跳转类型过滤,其中某一比特位为1时代表过滤相应类型的跳转指令。由于本文仅关心用户层的间接分支跳转,因此通过wrmsr指令将0x18D写入MBR_LBR_SELECT寄存器中,使得表 1所列的标志位为0。
标志位 | 名称 | 含义 |
1 | CPL_NEQ_0 | 发生在用户层的跳转 |
4 | NEAR_IND_CALL | 间接call指令 |
5 | NEAR_RET | return跳转指令 |
6 | NEAR_IND_JMP | 间接jmp指令 |
63:10 | — | 保留位必须置0 |
本文通过截获系统调用来实现检测的触发。当客户虚拟机进行系统调用时,由于接收到错误的系统调用号,产生SYSENTER_EIP_MSR fault,从而陷入到hypervisor中。此时hypervisor为虚拟机模拟该系统调用的实现,并在SYSENTER_EIP_MSR寄存器中存入新的异常值,保证下一次系统调用的截获。
当DomU进入内核时,本文通过配置control registers accesses字段,能够使得CR3寄存器中的值发生变化时产生VM-exit从而进入hypervisor的处理。此时VMCS会记录DomU中包括CR3和EIP在内的寄存器现场。因此本文使用不同的CR3值来区分所保护的进程,使用EIP地址判断是否进入内核范围(0xc0000000-0xffffffff)。
1.3 gadgets链检测为了防止LBR等寄存器的值被覆盖污染,在触发检测的第一时间hypervisor就要将虚拟机相关的硬件辅助信息存储到内存缓冲区。本文通过rdmsr指令获取LBR中记录的32条间接跳转地址对,参照kBouncer[11]中分析的gadgets链特征,实现了简单快速的gadgets链检测方法。
如图 2所示,LBR栈中某一条间接分支跳转指令的源地址BRANCH_FROM_IP[n]与其上一条间接分支跳转指令的目的地址BRANCH_TO_IP[n-1]之差即为一个执行指令片段的指令长度l。由于现实中的ROP攻击链一般受到注入空间、构造成本和通用性的限制,每一个gadget片段都不会很大,同时链接起来的gadgets数也不会很多。于是本文定义2个阈值Tg和Tc,将LBR栈32条地址对依次计算得到的31个指令片段长度序列按如下检测规则进行检验:
1) 每个包含指令长度l小于Tg的指令片段被视为一个gadgets。
2) 连续超过Tc个gadgets被视为构成ROP链。
在实际的应用场景中,无法找到2个恒定的Tg、Tc值来有效检测所有的ROP攻击。攻击者可以在库中精心寻找指令长度大于Tg的gadgets,构造跳转次数小于Tc的攻击链来绕过检测。如果本文简单地提高Tg或降低Tc,的确加强了检查的严格性,但是同时也会把正常的程序执行流也判定为ROP攻击,增加误报率。因此,本文参照ROPecker[12]统计结论,设置Tg=8,Tc=11作为默认值。另外在触发模块设计API接口,使得Dom0能按需调节这2个参数。
1.4 信息传递节1.3讨论了gadgets链检测的方法,优点是简单快速、开销极小而又有不错的检测能力。然而该方法并没有把LBR提供的准确地址跳转信息充分利用起来,另一方面文[3]提出一种构造long gadgets链的ROP攻击方法,使得当Tg=31、Tc=6时仍能绕过该方法。因此本文通过在Dom0中补充对DomU的控制流完整性检验,来加强检测的有效性,降低漏报率。在这之前必须先将hypervisor记录的虚拟机相关硬件辅助信息写入Dom0中的记录文件。为此本文设计了信息传递模块,通过重载Xentrace相关函数接收LBR数据,在特权域Dom0协助调用控制流完整性保护模块。
Xentrace是在Xen源代码中自带的一个工具,在编译时就会默认生成,用于辅助对Xen的底层分析和测试调优。Xen在关键位置有许多跟踪点,允许开发人员查看Xen内部发生的情况。启用这些跟踪点后,Xen会将跟踪信息写入Xen中的每个CPU缓冲区。然后在Dom0中的Deamon程序设置启用跟踪,并定期读取这些缓冲区并将其写入磁盘。本文通过改写重用Xentrace将所记录的数据存入trace buffer,发送VIRQ_TBUF中断通知Dom0事件记录完成。Xentrace生成的数据是二进制的格式,不能被直接理解。因此,本文为LBR数据设计了专门的解析器来解析其数据, 按照事先设定好的配置,提取间接分支跳转信息并转交CFI保护模块进行检测。当检测到ROP攻击后,解析器会提取Domain_id和TSC并转交于报警模块,以辅助定位威胁。
除了DomU的LBR的32条跳转地址对,hypervisor还需要为报警模块提供虚拟机ID和时间戳TSC,用以区分客户虚拟机。CR3寄存器中的进程页表基址也要记录下来用以辅助定位随机装载的共享库基地址。缓冲区中的记录格式如图 3所示。
1.5 CFI检测
在程序所执行的指令中,间接转移指令的目标地址是在运行时由寄存器或内存内容动态决定的,这就为攻击者对程序控制流的篡改和劫持提供了可能。文[8]由此提出了控制流完整性保护,其核心思想是限制程序运行中的控制转移,使之始终处于原有的控制流图所限定的范围内。ROP攻击实施的过程中必定会背离程序正常的控制流,因此本文通过保护Dom0中共享库的CFI来实现严格的ROP检测。在离线准备阶段,对常见的应用程序及其装载的共享链接库进行静态反汇编分析。合法跳转数据库的数据结构定义如下。
定义1 Entry_set数组。表示各个共享库中合法的跳入点的集合,其中每项元素是一个Bitmap结构。Bitmap的每一位对应共享库代码段中的一个偏移地址,值为1表示允许控制流在该段内偏移处跳入当前共享库。
定义2 Exit_set数组。表示各个共享库中合法的跳出点的集合,其中每项元素是一个Bitmap结构。Bitmap的每一位对应共享库代码段中的一个偏移地址,值为1表示允许控制流在该段内偏移处跳出当前共享库。
定义3 LibValidEdges<Source, Target>。一个Hash映射表(Hashmap),跳转源地址的偏移作为key,跳转目的地址的偏移作为value,以此存储共享库内每一对合法的跳转边。
对于静态分析获取的每一条间接分支跳转<Source, Target>,若属于图 4中的①类跳转边,则将Source地址所在共享库的Exit_set中Source地址对应的比特位置2;同样若属于图 4中的②类跳转边,则将Target地址所在共享库的Entry_set中Target地址对应的比特位置1;若Source、Target都属于同一共享链接库,即如图 4内的跳转边③,则将此分支跳转<Source, Target>计入LibValidEdges中。
需要注意的是本文合法跳转数据库存储的地址是相对于各个共享库起始地址的偏移,而Dom0中解析器提取的32条LBR间接分支地址是线性地址,需要减去对应的基址才能用于CFI检测。此外,当DomU中开启了地址空间随机化时,同一个共享链接库在不同应用程序的装载基址是随机的。因此,本文借助VMI技术获取跳转指令所属进程的内存描述符,从内存映射信息中收集各个共享连接库的装载基址。
操作系统内核均有相应的数据结构来存储进程的内存映射信息,本文以Linux系统为例,采取基于软件结构知识的VMI方法来获取数据元素(init_task)的初始地址、偏移量及数据类型等信息,线性扫描task_struct链表结构找到CR3对应的进程,直至重构完成数据结构中的每个数据元素。然后从task_struct结构体出发沿指针或引用的方向依次寻找mm_struct,直至vm_area_struct。最后将所有vm_area_struct结构体内装载共享库映射的首尾地址(start, end)按库名(name)记录在Liblist数组中,遵循图 5算法检验间接分支跳转的合法性。
本模块对DomU共享库中的跳转执行流进行了实时在线检测,弥补了gadgets链检测方法的局限,提高了检测的完备性。不同于其他粗粒度的CFI方法,本模块既不需要扩展编译器、修改内核,也不需要从客户虚拟机获取被检程序甚至源码,保持了虚拟机的透明性与隔离性。
2 实验评估 2.1 实验环境为了验证本方法的有效性以及评估本方案的性能开销,本文在一台采用Intel i7-6700 K 4 GHz处理器、32 GB内存的计算机上部署了Xen 4.6.0虚拟化环境。为其上的虚拟机分配单vCPU,2 GB内存,操作系统统一为32位Ubuntu14.04,Linux内核版本为3.14.60。
2.2 建立合法跳转数据库为了保护共享库中的控制流完整性,在IDA Pro上开发插件,静态分析表 2中所列的动态链接库,将其中的间接跳转按库名分别存储在节1.5描述的数据结构。
序号 | 名称 | 大小/k | ret | Ind. call | Ind. jmp |
1 | libc.so.6 | 1 824 | 5 128 | 994 | 742 |
2 | libpcre.so.3.13 | 448 | 278 | 66 | 98 |
3 | libdl-2.23.so | 16 | 34 | 18 | 25 |
4 | librt-2.23.so | 32 | 106 | 5 | 57 |
5 | libz.so.1.2.8 | 104 | 180 | 52 | 25 |
6 | ld-2.23.so | 160 | 295 | 97 | 33 |
7 | libssl.so.1.0.0 | 420 | 1 032 | 232 | 431 |
8 | libcrypto.so.1 | 2 308 | 5 513 | 697 | 350 |
2.3 有效性测试
本文选取了带有栈缓冲区溢出漏洞的程序[13]作为被攻击目标,编译时链接表 2中的共享库。参照ExploitBD上的10个shellcode[14],使用ROPgadget搜寻共享库中可用的gadgets片段并手动拼接成3类共120个payload,连同120条正常的输入构成实验样本,分别测试在仅启用模块1(gadgets检测模块)和同时开启模块1与模块2(共享库CFI检测模块)时的检测有效性,并与复现SIGDROP和HyperCropⅡ的2种方法进行对比。
SIGDROP通过计算CPU预测失败的ret指令条数在总指令条数中的占比来检测ROP攻击,检测规则简单,对一般ROP检测的有效性较低,且无法检测JOP攻击。HyperCropⅡ针对domU进程中的函数栈进行保护,以跳转地址中属于链接库的指令数目在栈中的占比作为检测依据,可以检测JOP攻击,但其检测规则仍不能很好地匹配gadgets链的特征,检测的有效性不高。另外该方法修改了栈的只读属性并设置断点,失去了对被检测虚拟机的透明性。这2种方法和本方法的gadgets链检测模块都需要提前设定关键参数的阈值,可以被攻击者针对性的构造long gadgets ROP攻击绕过。故本文引入CFI检测模块作为补充,采用更加准确严格的检测规则,保证了共享链接库中的控制流完整性,同时具备透明性和更高的检测准确率。实验结果如表 3所示。
编号 | 类别 | 总数 | 检出数量/(检测率/%) | |||
SIGDROP | HyperCrop Ⅱ |
模块1 | 模块1+模块2 | |||
1 | 正常输入 | 120 | 0/0 | 0/0 | 0/0 | 0/0 |
2 | 一般ROP攻击 | 40 | 24/60 | 26/65 | 29/73 | 37/93 |
3 | JOP攻击 | 40 | 0/0 | 25/63 | 28/70 | 39/98 |
4 | long gadgets ROP攻击 | 40 | 0/0 | 0/0 | 0/0 | 38/95 |
另外本文选取了4个具有内存破坏漏洞的常用程序部署在DomU上,在Metasploit的RopDb模块和ExploitBD公共漏洞发布平台上下载对应的PoC进行攻击测试,表 4所列结果表明本方法能有效地检测现实的ROP攻击。
漏洞程序 | 漏洞编号 | 模块1 | 模块1+模块2 |
Nginx1.3.9 | CVE-2013-2028 | √ | √ |
Glibc2.2.0 | CVE-2015-7547 | √ | √ |
FFmpeg3.2.1 | CVE-2016-10190 | √ | √ |
DNSTracer1.8 | CVE-2017-9430 | √ | √ |
2.4 性能开销
为了测试本方法对客户虚拟机带来的开销,本文采用UnixBench5.1.3作为为基准测试程序集,在被检测虚拟机DomU上进行20次性能测试,平均值如图 6所示。
当仅开启gadgets链检测模块时,引入系统的平均性能损失是4.5%。gadgets链检测与CFI检测同时开启时造成的平均性能损失是6.8%。其中对系统调用、进程创建和文件读写指标的影响较为显著。这是由于当系统调用时,本方案对其进行了截获与模拟,引入了额外的VM-Exit,在一定程度上影响了虚拟机性能。当利用VMI程序接口获取进程内存映射结构时,由于要遍历内核进程结构体,本系统对进程创建等操作更加敏感。另外本文改写Xentrace, 将LBR等硬件辅助信息写入Dom0的记录文件中,无疑也占用了一部分IO资源。
3 结论本文提出了一种面向云平台的硬件辅助ROP攻击实时检测方法。该方法利用Intel LBR硬件特性记录客户虚拟机敏感系统调用前的32条间接跳转信息,并在Hypervisor中进行ROP gadgets链检测。由于LBR的访问的开销可以忽略不计,再加上CR3硬件信息的辅助,准确地区分了不同虚拟机的数据,减小了VMI分析内存规模的数量级,使得整个检测流程做到实时高效。同时基于Xen虚拟化架构,重载了Xentrace将LBR记录写入特权域Dom0中的缓存文件,从而进行共享库中控制流完整性检测。该系统易于部署、对客户虚拟机DomU完全透明,设计模块可根据检查粒度和资源分配的现实情况灵活开启,充分利用了云环境的优势。实验结果表明该系统能有效地检测ROP和JOP攻击,同时保证平均开销低于7%。
[1] | CARLINI N, WAGNER D. ROP is still dangerous: Breaking modern defenses[C]//Proceedings of the 23rd USENIX Security Symposium. San Diego, USA: USENIX, 2014: 385-399. |
[2] | DAVI L, SADEGHI A R, LEHMANN D, et al. Stitching the gadgets: On the ineffectiveness of coarse-grained controlflow integrity protection[C]//Proceedings of the 23rd USENIX Security Symposium. San Diego, USA: USENIX, 2014: 401-416. |
[3] | GÖKTAŞE, ATHANASOPOULOS E, POLYCHRONAKIS M, et al. Size does matter: Why using gadget-chain length to prevent code-reuse attacks is hard[C]//Proceedings of the 23rd USENIX Security Symposium. San Diego, USA: USENIX, 2014: 417-432. |
[4] | BLETSCH T, JIANG X X, FREEH V W, et al. Jump-oriented programming: A new class of code-reuse attack[C]//Proceedings of the 6th ACM Symposium on Information, Computer and Communications Security. Hong Kong, China: ACM, 2011: 30-40. |
[5] | SNOW K Z, MONROSE F, DAVI L, et al. Just-in-time code reuse: On the effectiveness of fine-grained address space layout randomization[C]//Proceedings of 2013 IEEE Symposium on Security and Privacy. Berkeley, USA: IEEE, 2013: 574-588. |
[6] | VAN DER VEEN V, ANDRIESSE D, GÖKTAŞE, et al. Practical context-sensitive CFI[C]//Proceedings of the 22nd ACM SIGSAC Conference on Computer and Communications Security. Denver, USA: ACM, 2015: 927-940. |
[7] | TICE C, ROEDER T, COLLINGBOURNE P, et al. Enforcing forward-edge control-flow integrity in GCC & LLVM[C]//Proceedings of the 23rd USENIX Security Symposium. San Diego, USA: USENIX, 2014: 941-955. |
[8] | MASHTIZADEH A J, BITTAU A, BONEH D. CCFI: Cryptographically enforced control flow integrity[C]//Proceedings of the 22nd ACM SIGSAC Conference on Computer and Communications Security. Denver, USA: ACM, 2015: 941-951. |
[9] | JIA X Q, WANG R, JIANG J, et al. Defending return-oriented programming based on virtualization techniques[J]. Security and Communication Networks, 2013, 6(10): 1236–1249. |
[10] | WANG X Y, BACKER J. SIGDROP: Signature-based ROP detection using hardware performance counters[EB/OL]. [2017-05-30]. https://arxiv.org/pdf/1609.02667.pdf. |
[11] | PAPPAS V, POLYCHRONAKIS M, KEROMYTIS A D. Transparent ROP exploit mitigation using indirect branch tracing[C]//Proceedings of the 22nd USENIX Security Symposium. Washington DC, USA: USENIX, 2013: 447-462. |
[12] | CHENG Y Q, ZHOU Z W, MIAO Y, et al. ROPecker: A generic and practical approach for defending against ROP attack[C]//Proceedings of the 21th Annual Network and Distributed System Security symposium. San Diego, USA: NDSS, 2014: 1-14. |
[13] | LE L. Payload already inside: Datafire-use for ROP exploits[C]//Proceedings of Black Hat USA 2010. Las Vegas, USA, 2010: 49-54. |
[14] | EXPLOIT D. Archived shellcode for various operating systems and architectures[EB/OL]. [2017-05-30]. https://www.exploit-db.com/shellcode/?order_by=title&order=asc&p=Lin_x86. |