Press "Enter" to skip to content

【技术分享】蚂蚁安全光年实验室Usenix Security 2021中稿论文解读

本站内容均来自兴趣收集,如不慎侵害的您的相关权益,请留言告知,我们将尽快删除.谢谢.

 

近日,蚂蚁集团与南洋理工大学、中国科技大学联合论文《APICRAFT: Fuzz Driver Generation for Closed-source SDK Libraries》入选网络安全领域四大顶会之一USENIX Security 2021。

 

在黑盒的自动化漏洞挖掘过程中,闭源SDK库缺失源代码、API接口未知,而逆向成本高、效果差等问题,常常困扰安全研究者。

 

基于遗传算法,研究团队的APICraft 框架技术能对闭源SDK的API依赖关系进行最大程度的挖掘和还原,很好地解决了闭源SDK库的fuzz driver自动化生成难题,从而实现在0人工干预下挖掘漏洞。

 

通过运用这项技术方案,截止2021年2月,蚂蚁安全光年实验室已向Apple提交142个macOS系统库漏洞,并已收获54个Apple官⽅致谢。

 

蚂蚁光年安全实验室林性伟也因此获得安全届的奥斯卡大奖—2021年Pwnie Award “Most Innovative Research”奖提 名。

 

 

以下是他的技术分享

 

01

 

背景

 

在fuzzing过程中,安全研究员需要构建好⼀个应⽤程序⽤来接收fuzzer提供的fuzz input,这个应⽤程序我们称之为fuzz driver。过往的fuzzing相关研究⼤多针对于fuzzing引擎本身的优化提升,包括种⼦变异策略以及调度算法的优化,增加多维度的反馈,以及提升fuzzer速度等,这些研究已经将fuzzing研究变为红海,极其“内卷”。

 

而我们关注到,如何⾃动化地构建⼀个⾼质量的fuzz driver其实是⼀个同样关键的问题。直观来看,如果⼀个fuzz driver能够调⽤更多SDK提供的API,有更丰富的程序⾏为,那它在fuzzing过程中必然会有更⾼的覆盖率,从⽽更容易触发漏洞。因此如何⽣成⾼质量的fuzz driver是个值得深究的研究问题。

 

这篇⽂章主要解决了如何针对闭源SDK⾃动化⽣成⾼质量的fuzz driver问题。

 

1.1 实例

 

 

图 1:基于 CoreText库的两个 fuzz driver

 

图1是⼀个构建fuzz driver的例⼦,以macOS CoreText库为例,图1有两个fuzz drivers,分别是Consumer 1以及Consumer 2,将具体API简化,以伪代码形式来表现( 下面的序号标识了每个API调⽤,与图1相对应 ):

 

1.Consumer 1调用ProviderCreateWithData API创建了⼀个DataProvider对象prov;

 

2.基于prov对象创建了Font对象font;

 

3.最后计算出font对象的LeadingSpace的double值。

 

4.而 Consumer 2调用CreateFontDescriptor API创建了FontDescriptor对象desc;

 

5.再基于desc对象创建Font对象font;

 

6.最后计算font对象的LeadingSpace值。

 

 

图 2:不同的fuzz driver组合⽅式

 

图2是简化出来的API调⽤序列。(a)是原始的调⽤序列,(b)是我们将Consumer 1与Consumer 2进⾏了⼀个交叉变换,将Consumer 1的序列号1调⽤与Consumer 2的序列号4调⽤交换,但我们会发现,这个交叉变换并没有⽤。因为1与4的调换,只是改变了从raw data创建font对象的⽅式,并没有改变后续API调⽤的语义,后续的2->5,2->3都是没有变化的。所以我们其实是想要 (c)这种的组合,将3调⽤与5调⽤组合在⼀起。并且可能由于调⽤时序的不同会有意想不到的结果。⽐如先调⽤3计算LeadingSpace的double值,再调⽤5计算LeadingSpace可能会导致整数溢出漏洞。

 

从这个例⼦来看单纯依赖⼈⼯进⾏fuzz driver 构建耗费时间且容易出(error-prone)。需要⼀个⾃动化的框架来辅助完成这个fuzz driver构建过程。

 

02

 

系统总览

 

 

图 3:APICraft系统总览

 

我们设计并实现了APICraft系统⽤于针对闭源SDK fuzz driver⾃动化⽣成⼯作。图3是整体的系统框架总览。APICraft整体设计思路可以概括为Collect-Combine。

 

1. Collect :APICraft会对使⽤相关SDK的GUI应⽤程序进⾏动态trace,⽤于收集GUI应⽤程序的动态行为信息,包括GUI应⽤程序调⽤SDK API的data dependency以及control dependency等。

 

2. Combine :随后将这些dependency解析好之后进行多目标优化的遗传算法(Multi-Objective genetic algorithm)的变异进化。产生合乎我们要求的fuzz driver。

 

03

 

框架设计

 

框架设计章节将详细介绍APICraft框架的设计与实现细节。

 

3.1 API Function Dependency信息收集

 

⾸先是如何收集(Collect)API function dependency信息。APICraft最终目标是想自动化的完成fuzz driver 构建过程,而人工构建fuzz driver最核⼼的部分基于SDK提供的API构建API调⽤序列,API调⽤序列包含了data dependency以及control dependency。APICraft需要收集data dependency以及control dependency信息,⽤于作为后续的多目标遗传算法的变异进化的基因/染⾊体。

 

3.1.1 Data Dependency

 

3.1.1.1 定义

 

 

图 4:Data Dependency定义

 

在data dependency中,APICraft定义两个函数A与B有data dependency的关系在于,函数A的某个输⼊参数是函数B的输出参数/返回值,或者函数B的某个输⼊参数是函数A的输出参数/返回值。如果函数A与B存在data dependency,以图4的公式来表征,即函数A的输出参数/返回值会被⽤作函数B的输⼊参数。

 

APICraft定义了两类的API Data Dependency:

 

1. return value :函数A的返回值(return value)被⽤做函数B的输⼊参数;

 

2. output parameter :函数A的输出参数(output paramater,⼀般是以指针形式存在)被用做函数B的输⼊参数。

 

如果两个API函数满足data dependency关系,那这两个API函数就有时序调⽤关系。

 

3.1.1.2 解析

 

 

图 5:APICraft实现框架图

 

当APICraft收集完程序动态行为信息后,需要将信息解析成相应的data dependency。具体的解析步骤是:

 

1. 由图5所示,在预处理阶段,APICraft会通过SDK提供的头⽂件解析出每个API的参数与返回值的类型信息;

 

2. 而参数与返回值的值是由动态获取到的, APICraft基于function interposition机制实现了⼀套轻量级的动态trace框架 ,基于该trace框架,APICraft能够获取到动态运⾏过程中API函数进入前以及退出之后的参数与返回值信息,具体包括了thread id,nested level,以及会递归的将函数的参数值,返回值,输出参数值dump出来;

 

3. APICraft基于thread id来将不同线程的trace信息区分开;

 

4. APICraft会筛掉nested level大于1的API。APICraft针对的API函数都是SDK头⽂件⾥⾯提供的合法调⽤API。在动态trace过程,如果某个API不是由其他API所调⽤,即由我们的GUI应⽤所调⽤,他的nested level就是1,如果该API是在另外的API所调⽤的,那他的nested level就是2,以此类推。在fuzz driver⽣成的应⽤场景中,我们关注的是API函数如何正确地被GUI应⽤所调⽤,而不关注API内部调⽤的逻辑。APICraft需要演化学习的是GUI应⽤程序的程序行为逻辑,因此不关注SDK库内部调用的逻辑;

 

5. 区分输出参数:如果⼀个参数类型是指针,APICraft会监控该指针指向的内容在进API函数前,以及退出API函数之后是否有变化,如果有的话,则该参数会被判别为输出参数;

 

6. 结合类型(type)信息以及值(value)信息进⾏data dependency匹配:APICraft认为即使在类型信息⼀致的情况下,两个值为0的⽐对是不匹配的,因为值为0基本⽆意义。随后APICraft会将typedef给展开,如果类型不⼀致,APICraft会看两个比对对象的类型信息是否能够转换,如果(1) 两个比对对象的基本类型是⼀致的,只是修饰符不⼀样,比如const这种修饰词;(2)如果是指针类型的话,并且两者指针⼤小⼀致,或者对象之⼀指针是void*类型的。上述情形都是可转换的,两个对象可被匹配上。

 

 

图 6:Data Dependency解析算法

 

图6所示算法是APICraft Data Dependency解析算法,输⼊T是收集到的API函数的调⽤序列信息,输出R是解析完的data dependency的集合。

 

1.初始化阶段,R以及cache都将初始化为空;

 

2.算法会遍历每个函数A,在第8⾏中,算法会将output值不为0的函数加⼊cache中,cache是个字典类型,key为output的值,value为函数A的 output实例;

 

3.在第4⾏中,算法会遍历函数的每个输⼊参数(input parameter),用输⼊参数的值(value)当作key从cache中取出相应的函数的output,看看是否有函数的输⼊参数与另外函数的output类型与值匹配上的。如果有的话就加到集合R中。

 

3.1.1.3 Dependency推测

 

除了通过动态trace收集到的API data dependency关系,有些合理的API data dependency关系并不会被trace到(GUI应⽤程序没有相应的 API调⽤组合)。APICraft还会做dependency推测(inference)这⼀步。APICraft定义了三个推测规则:

 

1. R1: Dependency-based transition :如果函数A的output与函数C的输⼊参数相匹配,并且函数B的output与函数C的输⼊参数相匹配,以及又trace到,函数A的output与函数D的输⼊参数相匹配, APICraft会推断出,函数B的output跟函数D的输⼊参数能够相匹配并产生⼀组data dependency关系;

 

图 7:R1推测公式

 

2. R2: Type-based transition :当APICraft观察到函数A的output的类型信息与函数B的输⼊参数类型信息⼀致,这个时候APICraft会做个推测,因为这⾥没有值(value)信息,所以是个推测,推测出函数A的 output是函数B的输⼊参数;

 

图 8:R2推测公式

 

3. R3: Inter-thread data flow dependency :R3与图6的算法是⼀致的,只不过在这个规则下,会限定类型是指针,⼀般跨线程之间会传递指针,需要减少误报。

 

3.1.2 Control Dependency

 

APICraft收集到的Control Dependency主要是⽤来解决error code checking的:

 

1.API函数的输出参数(output parameter)或者返回值(return value)是指针类型,将对这个output值 进⾏⾮空判断(null check);

 

2.API函数的输出参数(output parameter)或者返回值(return value)是整数类型,并且是个status code的话,将进行动态污点分析来获取error code checking分⽀的表达式。(1)获取这个API函数的调用处(callsite);(2)通过静态分析找到⼀些error code checking的系统调用,比如exit,abort 等。这些basic block会被标记为checkpoint。(3)最后从调用处(callsite)开始进行taint analysis,因为正常的GUI应用程序会走正常分⽀,当走到checkpoint相应分⽀的时候将表达式取反, 让污点分析传播到checkpoint处。拿到对应的表达式。

 

3.2 Dependency Combination

 

APICraft将收集并解析完成的data dependency以及control denpendency进行Combination,再通过多目标优化遗传算法进行变异演化。

 

3.2.1 问题建模

 

APICraft将fuzz driver⽣成问题抽象成⼀个数学问题,利用多目标优化遗传算法(Multi-Objective Genetic Algorithm)进行求解。

 

具体而言,以GUI应用程序(调用相应 SDK提供的 API)的API函数使用方式为初始种群,对这些种群进⾏变异演化生成fuzz driver,通过判断生成的fuzz driver的优劣,将优越fuzz driver保留下来继续变异,最后生成满足要求的fuzz driver 用于fuzzing。我们认为 ⼀个高质量的fuzz driver需要满足三个⽬标 :

 

1. 多样性(Diversity) :多样性(Diversity)指的是fuzz driver能够调⽤⾜够多样的API使fuzz driver程序行为更丰富。即为了让生成出来的fuzz driver有更多不同的data dependencies,如果data dependencies能够组成loop,每条loop都会给这个目标加分数。图9所示的多样性(Diversity)的公式是生成的fuzz driver的有向多边图的边(即单个data dependency)的数量,加上这个图的圈复杂度。总体是要表征data dependency图(或者说fuzz driver的API调⽤)的多样性。

 

 

图 9:Diversity公式

 

2. 有效性(Effectiveness) :有效性(Effectiveness)是这三个指标中的唯⼀⼀个需要动态反馈信息的指标,其目标是要让⽣成的fuzz driver的API调用更合法有效。我们会给basic blocks中有调用其他函数的,以及这个basic block处于loop循环中的更多分数,因为我们觉得相对于核心代码而言error handling code在⼀个API函数中会执行更少的basic blocks,而核⼼代码会有更多的loop信息或者其 他函数调用。该指标是个动态的feedback,是需要将fuzz driver序列化成代码编译运行后得来的,我们对每个basic block评分(如图10所示):(1)调⽤其余函数以及处于loop循环中,评分3分;(2)调⽤其余函数或者处于loop循环中,2分;(3)两者均⽆则1分。

 

 

图 10:Effectiveness公式

 

3. 紧凑性(Compactness) :core dependency指的是从接收input file的API函数为起点,以此为根结点的展开的data dependency图。non-core dependency就是与这颗树无关的data dependency。F是core function(处于 core dependency中的函数)集合,f是集合⾥⾯的每个函数,If是每个函数的参数集合。k是每个input参数的无关函数数量,5是个经验值(即如果无关函数数量超过5,则该紧凑性(Compactness)指标得分为0)。

 

紧凑性(Compactness)指标⽬的是为了让fuzz driver去除冗余API调⽤,冗余API调用就是跟以接收input file API为起点的data dependency 图无关的API调用,即存在于non-core dependency图中的API调用。所以在core dependency的data dependency分数会高,non-core dependency中的data dependency分数会低。图11是Compactness的具体公式。

 

 

图 11:Compactness公式

 

3.2.2 多⽬标优化遗传算法 (Multi-Objective Genetic Algorithm)

 

APICraft采用了NSGA-II算法来对Diversity、Effectiveness、Compactness这三个目标进行多目标优化的遗传算法演进。

 

图12是整体的APICraft的多目标优化遗传算法,输入data dependency集合,输出是⼀系列的fuzz driver集合:

 

1. 25-31行即传统的遗传算法,先生成初始的种子集,选取初始种子集,然后开始变异,再选择存活下来的个体,继续变异,往复。直到到了我们限定的变异轮数。28行进行变异,29行选取最优个体;

 

2. 17-23行选取两个种⼦进⾏交叉变异;

 

3. 11-16行对交叉变异后的种⼦进行多目标优化的评分计算,然后筛出最优个体。12行计算目标评分,13行进行非支配排序算法,进行分层。14行计算拥挤度与拥挤度比较算子。15行筛选出来最优个体;

 

4. 1-10行就是对个体先进行序列化后,计算三个目标的分值。

 

 

 

图 12:APICraft的多⽬标优化遗传算法

 

04

 

实现

 

APICraft⼯程实现中核⼼之⼀是动态trace功能,动态trace是为了获取API函数的参数以及返回值。如图13所示,在hook中有两种机制:

 

1.Type-I需要两个hook点,函数的enter point以及exit point,enter point容易分析,但函数的exit point⽆法准确判断,因为⼀个函数可能会有多个exit点,单纯通过判断ret指令是无法精确判断exit点的,特别是当⼆进制程序被高度编译优化过。错误的exit点的hook机制会导致后续收集的nested level等信息都有误;

 

2.Type-II则没有这个问题,基于interposition的机制是中间有个媒介层在进⼊函数前接管,在退出函数之后也接管。我们就能准确拿到参数值以及返回值。Interposition机制的核心是会有⼀个跟被hook函数相同函数签名的替换函数,然后基于这个替换函数接管原函数的信息之后再调用原函数。在macOS上 APICraft用DYLD_PRELOAD跟DYLD_INTERPOSE机制来实现,在Windows上我们用的是detour来实现。

 

 

图 13:两种 hook机制

 

05

 

实验结果

 

5.1 多目标优化遗传算法

 

我们⼀共对5个攻击面进⾏了漏洞挖掘,包含了Image,Font,PDF,Audio,RTF,这里用Image这个攻击面来看看我们算法的实验效果,其他攻击面实验效果可查阅论文。

 

1. 图14左图是经过多目标遗传算法生成的fuzz driver跟人工写的fuzz driver在fuzzing过程中覆盖率比对。紫⾊的线是APICraft生成的fuzz driver,浅⾊线是Google Project Zero的安全研究员⼿写的fuzz driver,这个fuzz driver是研究员在对攻击面熟悉,并且通过逆向构建出来的fuzz driver。实验来看, 通过APICraft产生的fuzz driver在fuzzing过程中的覆盖率仍比P0顶尖安全研究员⼿写的fuzz driver实验效果卓越;

 

2. 图14右图是三个⽬标(Diversity、Effectiveness、Compactness)都结合起来生成的fuzz driver跟去掉每⼀个单⼀目标而生成的fuzz driver比对,比如绿色这条线是去掉多样性(Diversity)的覆盖率,去掉每个单⼀目标的实验效果没有三个目标都结合起来生成的fuzz driver在fuzzing过程中的实验效果好。

 

 

图 14:图⽚攻击⾯的多⽬标优化遗传算法实验效果

 

5.2 漏洞挖掘产出

 

基于APICraft⽣成的fuzz driver,我们进行了长达8个月的fuzzing。最终在macOS系统库5个攻击面上发现了 142 处漏洞,收到Apple 54 个官⽅漏洞致谢(该数据统计截⽌到论⽂投稿时,2021年2⽉)。

 

图15节选了⼀些漏洞,每⼀列分别是攻击面(Attack Surface),获取到的CVE号或者 Issue-ID,macOS的复现版本,漏洞类型,已经能在哪些 APP上⾯复现这些bug。

 

 

图 15:漏洞挖掘产出

 

06

 

总结

 

APICraft基于function interposition技术实现了轻量级的GUI应用程序动态行为收集框架,以及基于NSGA—II多目标优化遗传算法实现的fuzz driver自动化生成框架 。基于APICraft框架生成的fuzz driver在fuzzing过程中帮助我们挖掘到了macOS系统库 142 处漏洞,共收获Apple 54 个官⽅漏洞致谢。

 

APICraft项目主页链接为:https://sites.google.com/view/0xlib-harness 我们将在Usenix Security 2021会议完成之后开源代码。

 

Be First to Comment

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注