APP下载

ASLR机制脆弱性自动分析方法*

2020-05-06黄曙光潘祖烈

国防科技大学学报 2020年2期
关键词:控制流漏洞内存

黄 宁,黄曙光,潘祖烈,常 超

(国防科技大学 电子对抗学院, 安徽 合肥 230037)

随着信息技术的发展,软件漏洞的挖掘与利用成了一个热点问题。针对不同类型的漏洞利用技术,各种保护机制也层出不穷。但是,多年的漏洞利用实践证明,由于各方面条件的限制,依然存在许多可绕过这些保护机制,成功实施漏洞利用的技术手段[1]。

一般情况下,通过劫持程序控制流,跳转至指定内存地址,实现任意代码执行,需要在触发程序控制流劫持状态的同时,注入目标内存地址。地址随机化(Address Space Layout Randomization, ASLR)机制对加载于程序内存空间中的各模块进行随机化布局,导致攻击者无法准确定位目标代码的内存地址,从而阻止控制流劫持攻击[2-3]。但是,ASLR依然存在不少局限性[4-5]。受地址随机化的影响,内存中各模块的加载地址随机分布,但各模块的内部结构依然相对固定,是导致内存信息泄漏的重要原因。

随着程序分析技术的发展,近年来,出现多种针对二进制程序漏洞自动化分析与测试用例生成技术。早期的漏洞自动利用技术多依赖于对程序漏洞补丁的分析[6-8]。近年来出现了多种针对特殊类型漏洞利用及保护机制绕过的技术方案[9-10],但仍然缺少一款针对ASLR机制脆弱性进行自动化分析的方案。

文献[11]提出了基于符号执行的自动化程序漏洞利用(CRash analysis of Automatic eXploit, CRAX)方法。该方法使用选择性符号执行技术,引导程序运行并触发控制流劫持状态,生成控制流劫持点可达的输入测试用例。该方案的局限性在于,缺少对ASLR影响的分析,导致测试用例难以应用于ASLR环境下。

文献[12]针对控制流处于有效防御的环境时,提出了面向数据流的漏洞自动分析与利用方案FlowStitch。该方案在不改变程序控制流的前提下,利用已知漏洞,通过构造特殊数据,实现利用代码自动生成。但是,由于ASLR机制对内存模块布局的随机化处理,导致数据流分析无法应用于ASLR脆弱性分析过程。

文献[13]提出了首个针对Android系统的进行符号执行,实现漏洞自动利用的方案Centaur。该方案着重分析程序控制流状态变化过程,同时构造漏洞触发点可达的路径约束。该方案的局限性在于,未考虑漏洞触发与控制流劫持状态间的程序状态依赖,导致生成的测试用例不能实现控制流劫持攻击。

为了准确分析漏洞程序能否在ASLR环境下实现控制流劫持攻击,本文结合已有的漏洞自动分析与利用技术,针对四种内存泄漏场景,提出了一种基于控制流状态检测的ASLR脆弱性自动分析方法。

本文首先建立了有限状态机模型,描述被测程序的动态运行状态。然后,根据内存泄漏技术特点,筛选出ASLR绕过过程的状态依赖。在程序有限状态机模型的基础上,使用符号执行工具S2E[14-15]实现了地址随机化脆弱性分析(Address Randomization Vulnerability Analysis, ARVA)系统。原型系统检查内存泄漏状态的路径约束与控制流劫持状态的路径约束是否兼容,判断被测程序是否存在同时满足上述两种状态的路径。

1 ASLR环境下的程序有限状态机模型

根据常见的内存泄漏技术特点,结合污点数据传播和程序动态运行状态特征,本文提出了一种基于程序状态变迁的地址随机化脆弱性分析方法。该方法以上述各程序状态为分析单元,结合污点数据传播驱动程序动态运行过程,设计了针对ASLR环境下程序运行状态的有限状态机模型,如图1所示。该有限状态机(Finite States Machine, FSM)可用式(1)所示五元组进行描述:

M=(U,Σ,T,δ,u0)

(1)

T={S_State,T_State,Event,Constraint,Action}

(2)

式中:S_State和T_State分别表示T的初始状态和目标状态;Event表示S_State和T_State状态变迁依赖的输入事件,每次状态变迁依赖的Event属于Σ中的一个子集;Constraint表示监护条件或事件Event的参数等约束条件;Action表示状态变迁过程中的执行动作。Action对于状态变迁过程不是必需的,当条件满足时,也可以在不执行任何动作的情况下实现程序状态变迁。

状态集合U′中各状态的状态变迁过程所依赖的事件输入σ、约束条件c和执行动作a如下:

1)StateInput状态表示程序从开始接收外部数据(即污点源),到下一次任意污点数据传播或约束改变时刻之间的程序状态。外部输入事件可视为一种特殊的针对污点数据的操作事件σInput。事件σInput与StateInput状态转换的映射关系可如式(3)所示:

UxσInput→T:T_State=StateInput

(3)

由于程序执行过程可能不止一次地触发事件σInput,且每次触发事件σInput均会驱动程序进入StateInput状态,因此事件σInput和事件输入集合Σ的关系为:{σInput0,…,σInputn}⊆Σ。

图1 ASLR环境下程序有限状态机模型Fig.1 Program finite state machine model in ASLR environment

前置状态向输入状态变迁过程中的执行动作ainput:程序通过输入功能,将输入数据写入内存地址。

2)StateMem是从程序满足内存泄漏条件的时刻到目标内存信息泄漏时刻间的程序状态。本文总结了四种典型内存泄漏场景的StateMem状态依赖条件。

①容错攻击[16-17]。

事件输入σMem:循环执行事件集合EventLoop;内存读取事件EventReadMem;异常处理操作EventExcept。

集合EventLoop中的元素为二元组(Pos,Event),其中Pos表示事件Event的执行优先度,Pos越小,优先度越高。

约束条件cMem:{(Pos1,EventReadMem),(Pos2,EventExcept)}EventLoop∧(Pos1

执行动作aMem:EventLoop集合中的事件根据各自的执行优先级循环执行,直至内存读取事件EventReadMem读取目标地址。

②格式化字符串[18]。

事件输入σMem:调用格式化字符串函数事件EventFormat。

约束条件cMem:格式化字符串函数的格式化控制符参数为污点数据。

执行动作aMem:通过格式化字符串函数输出目标地址。

③任意地址读取。

事件输入σMem:内存读取事件EventReadMem。

约束条件cMem:事件EventReadMem的读取对象为带长度信息的数据结构 ∧(待读取数据结构长度信息为污点数据 ∨ 待读取数据结构指针为污点数据)。

执行动作aMem:事件EventReadMem读取指定内存信息。

④部分地址定位。

事件输入σMem:返回地址覆盖事件EventRetCover。

约束条件cMem:事件EventRetCover可通过污点数据覆盖函数返回地址。

执行动作aMem:覆盖返回地址的低地址部分。

3)StateHijack表示了程序控制流被劫持时刻的程序状态。程序处于控制流劫持状态,可实现任意地址跳转。

状态所依赖的事件输入σHijack:指令指针(Instruction Pointer, IP)寄存器值为污点数据。

约束条件cHijack:目标内存地址已泄漏。

执行动作aHijack:将目标内存地址写入IP寄存器。

后置状态:StateShell状态。

4)任意代码执行状态StateShell。该状态抽象描述了程序开始非法执行一段攻击者指定的代码到该段代码执行结束时刻间的程序状态。控制流劫持攻击的结果取决于跳转目的地址的可执行属性,当且仅当目标地址是可执行的,程序转入任意代码执行状态StateShell,表示控制流劫持攻击成功。

状态所依赖的事件输入σShell:IP寄存器值指向目标内存地址。

约束条件cShell:目标地址内存可执行。

前置状态S_StateShell:StateHijack状态。

ASLR环境下,基于内存泄漏的控制流劫持攻击要求程序状态集合U′是全体程序状态集合U的子集。集合U′至少要包含以下几种状态:

{StateEntry,StateInput,StateMem,StateHijack,StateShell}⊆U′

2 原型系统实现

本文使用符号执行技术,实现目标程序动态运行过程中的污点数据跟踪、程序状态监测与地址随机化脆弱性分析等工作。分析工作架构如图2所示。

图2 ASLR脆弱性分析架构Fig.2 Structure of ASLR vulnerability analysis system

分析架构通过符号执行组件对动态运行的目标程序状态进行监视与分析,构造满足地址随机化机制绕过的程序状态约束,并由约束求解器求解约束,生成测试用例。符号执行组件需要三项输入:可执行文件格式的目标程序,可触发目标程序内存泄漏状态的种子输入以及可触发控制流劫持状态的种子输入。

内存泄漏种子文件为可触发程序内存泄漏状态的输入数据。通过该种子文件,引导程序在动态运行过程中触发内存泄漏,并收集从入口点到内存泄漏状态的约束条件,构建目标程序的内存泄漏约束。

控制流劫持种子文件为可触发目标程序控制流劫持状态的输入数据。该过程不考虑ASLR对控制流劫持攻击的影响。本文将所有由外部传入目标程序的污点数据标记为符号化数据。目标程序在动态运行过程中,所有被污点数据污染的内存空间或寄存器会被标记为符号化内存或寄存器。通过检查某一内存地址或寄存器的符号化属性,可判断内存地址或寄存器的可控性。

针对目标程序进行分析时,分别通过内存泄漏种子文件和控制流劫持种子文件驱动目标程序动态运行,生成内存泄漏状态约束ConstraintMem与控制流劫持状态约束ConstraintHijack。约束求解器求解上述约束间的兼容性,判断两者是否具备在同一程序路径触发的可能性。当且仅当上述两种约束满足式(4)所示关系时,表示两者互相兼容。

ConstraintMem∧ConstraintHijack=True

(4)

若两种约束互相兼容,表明目标程序存在至少一条路径,可同时抵达内存泄漏状态和控制流劫持状态,即目标程序满足ASLR环境下的控制流劫持攻击条件。

为了降低符号执行对目标程序控制流劫持点检测的时间开销,符号执行组件采用了经过路径选择算法优化的导向式符号执行技术。以种子输入作为目标程序的输入文件,引导目标程序沿着确定的程序路径动态运行,直至触发相应的程序状态。图3是通过种子输入引导符号执行触发相应程序状态的过程。

符号执行组件用于监视程序动态运行状态,并分析程序状态变迁过程是否满足基于内存泄漏的地址随机化绕过的依赖条件。在此过程中,符号执行组件收集程序运行的路径约束,并根据程序状态,构造满足状态变迁条件的数据约束。符号执行组件的工作架构如图4所示。

符号执行组件对程序状态分析的过程,是一种面向过程模式的FSM结构实现过程。当程序状态满足变迁约束,并触发状态变迁操作时,当前状态(源状态)执行退出动作。每个源状态的退出动作由两部分组成:根据源状态构造数据约束;根据事件及事件约束选择目标状态。源状态退出动作过程如算法1所示。

(a) 控制流劫持种子驱动程序运行过程(a) Control-flow hijack seed input direct the running path of program

(b) 内存泄漏种子驱动程序运行过程(b) Memory leakage seed input direct the running path of program图3 种子输入引导符号执行触发程序状态的过程Fig.3 Seed input trigger the program states in symbolic execution

图4 符号执行组件工作架构Fig.4 Structure of symbolic execution parts

算法1 当前程序状态(源状态)退出过程Alg.1 Exit process of current program state (source state)

算法1通过下述操作为程序状态退出过程建立数据约束:

Constraint=Eq(Value,Addr)

其中:Value为关键内存地址在相应的状态退出过程中需要满足的条件值;Addr表示约束构建的目标内存地址或寄存器。

完成数据约束构造后,通过事件输入判断程序状态,实现程序状态变迁逻辑。该过程如算法2所示。

3 实验评估

本文使用S2E作为原型系统ARVA的符号执行引擎,针对快速模拟器(Quick EMUlator, QEMU)下运行的目标程序及其系统环境进行全系统模拟的基础上,实现程序状态监测与ASLR脆弱性分析。本文选取了8个实际的漏洞利用攻击样本对ARVA原型系统进行评估。实验中,原型系统运行在配备了3.40 GHz Intel Core i7-6700 CPU、8 GB 内存、250 GB 硬盘的计算机上。

算法2 程序状态变迁过程Alg.2 States conversion process

目标程序与系统均运行于QEMU虚拟机中,各样本的漏洞编号、运行环境与目标程序如表1所示。

表1 实验样本信息Tab.1 Information of experimental sample

3.1 ASLR脆弱性分析结果

本文选取了近年来影响较大的控制流劫持攻击漏洞,对ARVA原型系统及其理论的有效性进行验证评估。实验中,内存泄漏种子文件触发各样本程序内存泄漏状态信息如表2所示。

表2中,ASLR环境表示样本程序的依赖系统对程序内存空间的随机化效果。由于早期的Windows系统未设置ASLR机制,因此样本程序不受地址随机化的影响。Windows XP系统的ASLR机制只能实现堆栈等部分内存空间的随机化效果,本文将该环境称为部分随机化环境。在Windows 7以后的系统中,ASLR机制基本实现了针对全部内存空间的随机化效果,本文将实现这一效果的随机化环境称为整体随机化环境。最终泄漏目标地址表示内存泄漏种子文件引导样本程序执行至最后一次内存泄漏状态时,泄漏的目标地址。

由控制流劫持种子文件触发各程序控制流劫持状态信息如表3所示。

表3中,控制流劫持目标地址表示样本程序进入控制流劫持状态时,下一指令的跳转目的地址;目标跳转指令为该目的地址处对应的汇编指令;控制流劫持攻击类型表示样本程序在控制流劫持种子文件驱动下执行的攻击类型。

表4列举了各样本程序从初始状态到控制流劫持状态变迁过程中,StateMem状态的触发次数以及每次触发该状态时泄漏的内存信息。

根据表2、表3与表4的信息,仅MS-06-055与CVE-2010-3333的控制流劫持攻击选用了覆盖返回地址的方式。原因如下:

1)Windows 2000未设置任何地址随机化机制。MS-06-055的控制流劫持种子可通过JavaScript实现堆喷射布局shellcode以及固定值0×0c0c0c0c覆盖返回地址,导致任意代码执行。这一过程不涉及内存泄漏。

表2 样本程序内存泄漏状态信息Tab.2 Information of memory leakage of experimental sample

表3 样本程序控制流劫持状态信息Tab.3 Information of control-flow hijack of experimental sample

表4 基于内存泄漏的样本程序状态变迁过程Tab.4 States conversion of memory leakage of experimental sample

2)Windows XP SP3设置了针对堆栈等部分内存区域的地址随机化。CVE-2010-3333实验中,控制流劫持种子将Shellcode布局于栈内存中。但由于堆栈随机化影响,需要通过跳板指令jmp esp确定栈内存的位置(即定位栈空间地址的前四位)。Office 2003中,存在不受随机化影响的地址0x7d1f5fb7固定为jmp esp指令。当程序处于控制流劫持状态时,将返回地址覆盖为0x7d1f5fb7,执行跳板指令后,可定位栈空间前四位地址,驱动样本程序开始执行栈内存中的Shellcode,触发任意代码执行状态。

CVE-2014-0322、CVE-2015-3090、CVE-2015-5119与CVE-2015-5122实验分别验证了整体随机化环境下的漏洞可利用性。上述漏洞均可通过Action Script脚本触发IE的Flash插件漏洞并实现漏洞利用。本文分别选取Kernel32.dll与Flash32.dll模块作为最终内存泄漏目标。内存泄漏种子文件可覆盖Flash Vector数组长度,导致任意内存读写。上述样本程序中,基于容错攻击的CVE-2014-0322触发了一次内存泄漏状态;其余样本至少触发两次以上内存泄漏状态。

与其他实验相比,CVE-2014-6332实验涉及特殊的IE浏览器沙盒机制。本文针对该机制构造的控制流劫持种子文件在触发样本控制流劫持状态后,不会转入汇编指令层面的任意代码执行状态。控制流劫持种子文件通过将IE浏览器安全标志置于位置0,使用VBScript脚本可调用系统的任意功能。该攻击方法被称为数据虚拟执行DVE。

根据表4的信息,除MS-06-055实验不涉及ASLR环境外,其余各实验的内存泄漏约束与控制流劫持约束的判定结果均为互相兼容。该结果表明,这些实验样本均至少拥有一条程序路径,同时满足内存泄漏和控制流劫持的条件。在此路径下,ASLR机制可被绕过。

上述实验表明, ARVA原型系统可有效识别部分地址定位、容错攻击、任意地址读写等内存泄漏状态,以及覆盖返回地址、ROP、ret-to-libc等控制流劫持类型。另一方面,ARVA通过求解内存泄漏状态约束与控制流劫持状态约束,可判断两者的约束是否兼容。实验结果表明,基于内存泄漏的ASLR绕过过程中,可能需要触发不止一次内存泄漏状态,才能非法获取目标内存信息。

3.2 案例分析

本文挑选了CVE-2014-6332案例来对ARVA的检测过程与效果进行详细阐述。此漏洞利用任意地址读写实现ASLR绕过;通过DVE,Heap Spray等攻击技术实现控制流劫持。本文的系统能准确识别样本程序的内存泄漏状态与控制流劫持状态,构造并判断两种状态约束的兼容性。

案例CVE-2014-6332漏洞程序ASLR脆弱性分析。此漏洞是一个整数溢出漏洞。该漏洞可通过篡改IE浏览器的安全模式标志位,绕过浏览器沙盒保护,进而导致脚本文件获取任意功能执行权限。

CVE-2014-6332实验的种子文件为VBScript脚本文件。其中,内存泄漏种子输入的目标为:引导漏洞程序在ASLR环境下准确定位IE安全模式标志位的地址;控制流劫持种子文件的目标为:引导漏洞程序执行任意功能。

IE浏览器通过OLEAUT32.dll对VBScript中的数组进行管理。假设有数组arr,其初始长度为a0;后又将数组arr的长度更改为a1=a0+0×80000000,OLEAUT32将根据a1-a0=0×80000000计算数组arr的新索引值。由于0×80000000被系统默认为有符号整数,换算成十进制数为0,因此数组arr的实际大小仍为a0。此时,通过arr的索引值,可实现越界读写。

内存泄漏种子文件触发的程序状态变迁过程为:

StateInput状态:向进程内存中布置两个错位分布的污点数据数组arrA与arrB。两个数组在内存中的布局如图5所示。

图5 数组arrA与arrB结构分布Fig.5 Layout of arrA and arrB

构建数据约束:Constraint1=Eq(arrA, &arrA)∧Eq(arrB, &arrB)。

VBScript语言的数据存储时,每个数据都占据16字节,其中,前8个字节表示数据类型,后8个字节表述数据值。

StateInput状态:通过种子文件布局一个sub类型函数func,并将函数地址&func存储于arrA(a1)。

构建数据约束:Constraint2 =Eq(&func, &arrA(a1))。

StateInput状态:typeof(&func)= 0x1(NULL类型)。构建数据约束Constraint3=Eq(0x1, &arrA(a1-1))。

StateInput状态:arrB(2) =1.740 885 347 313 24E-310(arrB(2)数据值=arrA(a1+2)类型值= 0×200C)。

构建数据约束:Constraint4=Eq(1.740 885 347 313 24E-310, &arrB(2))。

StateInput状态:布局Safe Array型数组myArr起始地址为0×0,包含0×7ffffff0个元素,每个元素大小为1 Byte。

构建数据约束:Constraint5=Eq(myArr, &myArr)。

StateInput状态:arrA(a1+2)=&myArr。

构建数据约束:Constraint6=Eq(&myArr,arrA (a1+2))。

检查任意地址读取类型依赖的约束条件,数组arrA满足下述条件:

数组arrA带长度信息的数据结构∧数组arrA长度信息a1为污点数据。

因此,ARVA判断数组arrA满足任意地址读取的触发条件。

StateMem状态:利用arrA数组越界读写泄漏&func+ 12处CScriptEntryPoint对象指针。

构建数据约束:Constraint7=Eq(&CScriptEntryPoint, &func+ 12)。

StateMem状态:泄漏&CScriptEntryPoint+20处COleScript对象指针。

构建数据约束:Constraint8=Eq(&COleScript, &CScriptEntryPoint+20)。

StateMem状态:泄漏&COleScript+0×174处的IE安全模式标志位地址。

构建数据约束:Constraint9=Eq(& SecurityBit, &COleScript+0×174)。该地址在正常权限下仅可通过Safe Array数组进行写操作。

StateInput状态:myArr[&SecurityBit]=0。

构建数据约束:Constraint10=Eq(0,myArr[&SecurityBit])。

内存泄漏的路径约束构建过程中,共触发6次StateInput状态,3次StateMem状态,并在触发StateMem状态时针对状态依赖的事件输入与约束条件进行了1次检查。

ARVA在该过程中建立的污点源数据约束如式(5)所示。

srcConstraint=Constraint1∧Constraint2∧

Constraint3∧Constraint4∧

Constraint5∧Constraint6∧

Constraint10

(5)

ARVA建立的内存泄漏数据约束如式(6)所示。

memConstraint=Constraint7∧Constraint8∧

Constraint9

(6)

当该漏洞程序处于任意代码执行状态StateShell时,指定的代码为VBScript脚本文件中任意功能调用代码。由于DVE利用技术的特殊性,可认为该案例中的控制流劫持状态StateHijack与StateShell是同时触发的。控制流劫持种子文件触发的程序状态变迁过程为:

StateHijack状态:ShellExecute “cmd.exe”。

CVE-2014-6332的最终数据约束如式(7)所示:

dataConstraint=ConstraintMem∧ConstraintHijack

=srcConstraint∧memConstraint∧ConstraintHijack

(7)

其中,ConstraintHijack因不涉及IP寄存器的污点传播,其默认值为True。求解约束dataConstarint值为True,表示内存泄漏约束ConstraintMem与控制流劫持约束ConstraintHijack兼容,因此可得出结论,CVE-2014-6332可通过内存泄漏绕过地址随机化,实现控制流劫持攻击。

3.3 时间开销分析

在原型系统ARVA中,测量时间t1定义为内存泄漏种子输入驱动样本程序开始运行,至求得内存泄漏约束所用时间;t2定义为控制流劫持种子输入驱动程序开始运行,至求得控制流劫持约束所用时间。同时,本文也在原生S2E系统中,以结合了ASLR绕过和控制流劫持功能的脚本文件为输入,运行同样的样本程序,并记录样本程序的运行时间t′。具体数据如图6所示。

图6 ARVA与原生S2E运行时间开销对比Fig.6 Comparison of running time of ARVA and S2E

根据图6数据,MS-06-055不涉及地址随机化绕过,ARVA针对其控制流劫持约束求解时间t2大于S2E的运行时间。CVE-2010-3333的使用部分地址定位,内存泄漏约束求解过程与控制流劫持约束求解过程基本一致,两者的时间t1与t2基本相等,与S2E的运行时间t′也基本持平。

CVE-2012-1876、CVE-2014-0322、CVE-2014-6332、CVE-2015-3090、CVE-2015-5119与CVE-2015-5122等实验时间开销基本呈现下述两种特点:t1>t′>t2;t1+t2≈2t′。

上述特点说明,ARVA针对样本程序的地址随机化脆弱性检测的时间开销主要集中于内存泄漏约束的构建与求解方面。

此外,由于ARVA的控制流劫持约束求解过程不考虑地址随机化对样本程序的影响,而原生S2E系统在运行同一样本程序时,种子文件结合了地址随机化绕过与控制流劫持两方面功能,导致ARVA的控制流劫持约束求解时间t2小于S2E运行时间t′。

4 结论

本文提出了一种基于有限状态机的ASLR机制脆弱性分析方法。该方法能够分析目标程序是否可通过内存泄漏技术绕过ASLR保护,实现控制流劫持攻击。本文使用有限状态机模型描述程序状态变迁过程,分析通过内存泄漏导致ASLR绕过的程序状态依赖,在此基础上,针对四种常见的内存泄漏场景分别建立了内存泄漏状态的进入与退出条件,使该模型更好地适用于不同的漏洞攻击模式。根据上述理论与模型,本文实现了一套基于符号执行工具S2E的ASLR脆弱性分析原型系统ARVA。该系统针对目标程序的内存泄漏状态与控制流劫持状态分别进行约束构建,并对二者的兼容性进行求解,检查目标程序是否满足地址随机化环境下实施控制流劫持攻击的条件。通过对若干实际漏洞程序的实验表明,本文方法能够准确检测到被测程序的动态运行状态,求解相关程序状态约束,并有效识别ASLR环境下漏洞程序的可利用性。

猜你喜欢

控制流漏洞内存
漏洞
抵御控制流分析的Python 程序混淆算法
抵御控制流分析的程序混淆算法
基于控制流的盒图动态建模与测试
笔记本内存已经在涨价了,但幅度不大,升级扩容无须等待
“春夏秋冬”的内存
基于Petri网数据流约束下的业务流程变化域分析
三明:“两票制”堵住加价漏洞
漏洞在哪儿
高铁急救应补齐三漏洞