首页
论坛
课程
招聘
[翻译]使用Fuzzing在闭源Windows软件中寻找漏洞
2019-11-10 19:22 10582

[翻译]使用Fuzzing在闭源Windows软件中寻找漏洞

2019-11-10 19:22
10582

模糊测试已被证明是用来发现软件bug的一种有效技术。如今,将模糊测试整合到您产品的开发生命周期中对于发现bug是十分重要的,这些bug不能通过经典的质量保证(QA)技术发现,如代码审查,源代码注释,和单元测试。

 

本文中,我们会向您展示如何使用覆盖率指导的模糊测试发现Windows闭源软件中的漏洞。我们利用WinAFL并以ACDsee Photo Studio Standard 2019为例。

 

本文对QA专家以及想要提高测试质量的逆向工程师和开发人员很有用。

模糊测试理论

首先,什么是模糊测试?维基百科是这样定义的:

模糊测试(Fuzzing or fuzz testing)是一种自动化软件测试技术,涉及提供无效的、非预期的或随机的数据作为计算机程序的输入。然后监视程序是否发生异常,例如崩溃,内部代码断言失败或潜在的内存泄漏。

 

这类测试简单但十分有效,因为它不需要预先了解系统的行为。另外,模糊测试可能是发现完全闭源软件中漏洞的唯一测试方法。

 

根据您对测试程序结构的了解程度,模糊测试分为三种类型:白盒,灰盒和黑盒模糊测试。

 

由于本文的目的,我们使用了灰盒模糊测试,又称为覆盖指导的模糊测试。该类模糊测试使用软件插桩来追踪向测试目标提供每个输入时的代码覆盖范围。覆盖指导的模糊测试可应用于确定且自包含的目标,该类目标每秒可以执行数十次或更多次(例如,图像格式解析器)。

 

模糊测试需借助于自动化漏洞发现工具(也被称为模糊器“fuzzers”)而执行。“American fuzzy lop”或AFL 模糊器,是进行覆盖指导的通用模糊测试的最有效工具之一。该模糊测试工具有效的发现了真实世界中的bug,因为他使用了编译时算法和通用算法来自动化发现测试用例。

 

在我们的案例中,我们对ACDSee Photo Studio Standard 2019执行模糊测试。ACDSee是ACD Systems International Inc为Windows,Macos和iOS平台开发的图像编辑解决方案。在我们的案例中,我们使用版本号为22.1.0.1159的64位ACDSee Photo Studio二进制程序作为目标。

 

图片描述

 

我们对这个应用程序的查看功能特别感兴趣。我们假设该应用程序自己实现了图像解析功能,而这就是我们准备模糊测试的功能。

 

当测试ACDSee时,我们的主要目标是生成许多图片并通过图像查看器打开它们。假设我们可以通过放置随机数据到图片文件的内部结构来创建许多样本。但逐个打开它们的效率很低,因为我们没有关于程序行为的任何信息,即没有目标应用覆盖范围的任何信息。如果我们可以得到程序的覆盖范围,我们就可以创建更智能的测试用例。从而,我们可以提高模糊测试的整体效率。

 

这是AFL派上用场的地方。它基于被测应用程序的覆盖范围实现来实现输入数据的变异。为了得到覆盖范围,AFL需要在编译时对二进制程序进行插桩。在我们的案例中,我们没有应用程序的源代码。但是非常幸运,AFL有一个出色的分支,称为WinAFL。该模糊测试软件依赖于由DynamoRIO收集的覆盖范围,在本例中我们也使用它。WinAFL也支持其他插桩方法,例如IntelPTSyzygy

 

这里的目标进程是ACDSeeStandard2019.exe,它接受一个字节数组作为输入,并使用测试的API对其做一些有意思的操作。

 

让我们看一看通常模糊测试的流程:
图片描述

 

现在,整个思路应该很清楚了,我们可以开始寻找模糊测试的入口点了(例如,创建一个工具程序,用于触发我们想要模糊测试的功能)。

逆向并寻找攻击面

WinAFL已经有了一个变种生成器和覆盖分析器,所以我们要做的只是正确地应用于ACDSee Photo Studio。让我们看看所测试应用程序的内部以及看看我们能做些什么。我们寻找的是解析输入文件的代码。

 

作为一名逆向工程师,您可以使用自己喜欢并熟悉的任何方法。这里有一些可能的选项:

  • 使用IDA ProGhidra和[radare2])(https://rada.re/r/)进行静态分析
  • 使用WindDBGx64dbg调试代码(在运行时设置断点并分析函数参数是非常有帮助的)
  • 使用辅助工具,如API监视器,进程监视器和覆盖率工具。

例如,当文件被读取时,您可以使用ProcMon开始分析栈调用。只需为输入文件添加一个过滤器并在查看器中打开它。
图片描述

 

注意名为IDE_ACDStd.apl的奇怪模块。它实际位于Plugins文件夹中,它仅是个有一些有趣导出函数的常规dll:
图片描述

 

当然,ACDSeeStandard2019.exe利用了一些插件系统,并且似乎IDE_CADStd.apl只是一个负责处理大多数流行图像格式的常规插件。实际上,我们甚至可以找到他的手册!

 

图片描述

 

当然,这不是一个SDK手册。但至少,它描述了此插件的功能。拥有插件的SDK意味着我们可以轻松的编写出用于模糊测试的工具,不幸的是,我们没有。

编写工具

该工具应该可以使用IDE_ACDStd.apl模块的导出函数来正确地加载,处理和关闭图像。工具应该为fuzzer定义一个入口点,fuzzer每次迭代的时候会调用该入口点函数。换句话说,当WinAFL生成新的输入时,利用工具来检查生成的输入是否导致了新的代码覆盖范围。

 

让我们分析下打开图片时将执行那些IDE_ACDStd.apl的导出函数。我们利用IDAPython的断点功能来确定:

import idautils
condition = """
print("In BP: %s")
return False
"""
def bp_mark_exports():
  for exp_i, exp_ord, exp_ea, exp_name in idautils.Entries():
    address = get_name_ea(0, exp_name)
    print("[+] Set BP: 0x%x, %s" % (address, exp_name))
    add_bpt(address, 0, BPT_SOFT)
    enable_bpt(address, True)
    SetBptCnd(address, condition % exp_name)
bp_mark_exports()

我们可以清楚的看到这种模式

...
In BP: IDP_OpenImageW
In BP: IDP_GetImageInfo
In BP: IDP_GetImageInfo
In BP: IDP_GetPageInfo
In BP: IDP_PageDecodeStart
In BP: IDP_PageDecodeStep
In BP: IDP_PageDecodeStep
In BP: IDP_PageDecodeStep
...
In BP: IDP_PageDecodeStep
In BP: IDP_IsAutoRotated
In BP: IDP_IsAutoRotated
In BP: IDP_PageDecodeStop
In BP: IDP_CloseImage
...

我们可以确定以下内容:

  • 使用IDP_OpenImageW打开图片
  • 在某些时候可以看到IDP_PageDecodeStart
  • 我们得到了许多IDP_PageDecodeStep调用,这是繁重工作的所在。
  • 在最后,我们可以看到IDP_PageDecodeStopIDP_CloseImage

顺序是很清楚的,但仍然至少存在两个问题组织我们编写工具。

 

第一个问题是我们可以在每个函数中看到下面这个检查:
图片描述

 

我们把全局变量命名为g_isInit,因为如果它没有被设置,代码就不会执行。使用交叉引用,我们可以看到全局变量仅在IDP_Init中设置一次设为1。当IDP_Init被调用时,一些看起来随机的数据作为参数被传递给它。IDP_Init通过计算hash对该数据进行检查,如果数据合法,则设置g_isInit为1。绕过该检查并强制成功加载dll并不容易。

 

第二个问题是我们不知道我们感兴趣的所有函数的参数。因此,我们需要逆向他们。这是我们最终得到的应做工作的代码片段:

 

图片描述

 

imageClass是图像的实例,我们应该使用它作为后续调用的参数,在代码最后应该使用IDP_CloseImage将其关闭。fc是需要我们填写的输入结构:
图片描述
关键域:

  • imageData——指向文件的原始数据
  • imageSize —— 文件的大小
  • checkSizeCallback —— 当插件代码需要检查图像大小时调用的回调函数。插件的代码会调用此回调函数并把检查值作为参数,而不是直接使用imageSize结构体成员直接检查大小。
  • self ——应该指向此结构的起始地址

图片描述

 

我们不会提供工具的全部源代码以免对产品厂商造成任何损害,但是如果您已经仔细阅读到了这里,你应该已经了解其工作原理。

 

该工具仅执行以下五个操作:

  1. 加载IDE_ACDStd.apl库并调用IDP_Init
  2. 使用文件的命令行路径从fuzzer读取图像数据
  3. 调用 IDP_OPENImageWIDP_PageDecodeStart来初始化迭代器
  4. 多次调用IDP_PageDecodeStep直到它返回错误
  5. 调用 IDP_PageDecodeStopIDP_CloseImage来释放资源

注意:与经典的AFL不同,当您可以访问源代码时,为闭源二进制编写工具是困难的甚至设计上是不可能的。在这种特殊的情况下,我们花费了几天时间用于逆向和写工具上。

Fuzzing本身

最后,我们进入到探索中最有趣的部分——fuzzing本身。通常,在这个环节,我们需要收集输入语料库,将其最小化,并将其用作变异器的“种子”。您可以从AFL的演示用例开始。但事实证明,一些测试用例已经导致崩溃。

 

因此,出于教学目的,让我们尝试一种低效但更有趣的方法。如果我们不提供任何语料库怎么办?严格地说,您不能这样做,因为WinAFL不会在输入空文件夹作为参数时运行。然而,我们可以提供一些虚构的输入文件——例如,一个包含“123”的文件——并等待变异器生成用于在目标程序中引发覆盖率的输入文件。最终,这些新文件应该看起来像图像。

 

启动fuzzer的命令行看起来是这样的:

afl-fuzz.exe -D z:\s\tools\dr70\bin64\ -i in_none -o out_none -timeout 15000+ -- -target_module harness.exe -target_method parseFile -coverage_module IDE_ACDStd.apl -- z:\s\acdsee\harness.exe "C:\Program Files\ACD Systems\ACDSee\22.0\PlugIns\IDE_ACDStd.apl" @@

 

我们使用插桩的基本块类型而不是edge,因为该术语在我们的特定情况下不是很重要。

 

同样,您可能想要启用页堆
图片描述

 

多线程模糊测试了10到20分钟后,我们发现了一些奔溃!这些崩溃的原因是一些TGA文件:

$ file id_000004_00
id_000004_00: Targa image data - Map 8224 x 8224 x 32 +65280 +6104 - top " b \005 "

 

我们在一台8线程的笔记本电脑上运行了3个模糊测试会话。您可以使用winafl-plot.py来生成有用的报告。这是一个模糊测试报告:
图片描述
图片描述

 

最有趣的指标是:

  • 总路径——DynamoRIO的工具探测到的覆盖路径数
  • 唯一崩溃——崩溃的次数(唯一性取决于覆盖路径;通过多个覆盖路径可以达到同一个逻辑错误)
  • exec/sec—— 每秒迭代的次数

我们从WinAFL收集了1812个独特的测试用力,并根据Linux file工具的输出对它们进行了排序:
图片描述

total: 1812
unrecognized: 944
PC bitmap: 383
TGA: 355
TIFF: 99
PCX: 25
PIC: 6

 

手动查看这些崩溃是不可行的。您可以使用像!exploitableBugId这样的工具对其进行测试。使用!exploitable,我们测试了1812个用例,并形成了84个报告,其中27个被标记为“可利用”。
图片描述

 

我们发现的bug之一是CVE-2019-15293。开始于IDE_ACDStd!IEP_ShowPlugInDialog+0x000000000023d060的User Mode Write AV 。利用该漏洞将导致未经授权的信息泄露,信息修改和服务终止。此错误的完整代码可以在这里找到。

结论

不幸的是,使用传统的测试方法无法发现所有的bug,因此第三方Windows应用程序仍然包含bug,这使它们容易成为犯罪分子的目标。但是,在软件的安全测试中使用模糊测试,将使您显著提高解决方案的质量。正如你所看到的,模糊测试使我们能够发现在ACDSee Phot Studio 2019中的27个漏洞,一些已经被MITRE公司分配了CVE编号。

原文链接: https://www.apriorit.com/dev-blog/640-qa-fuzzing-for-closed-source-windows-software
编译:看雪翻译小组 sudozhange
校对:看雪翻译小组 玉林小学生


第五届安全开发者峰会(SDC 2021)10月23日上海召开!限时2.5折门票(含自助午餐1份)

最后于 2019-11-10 19:23 被sudozhange编辑 ,原因:
收藏
点赞1
打赏
分享
最新回复 (7)
雪    币: 7791
活跃值: 活跃值 (6765)
能力值: ( LV12,RANK:210 )
在线值:
发帖
回帖
粉丝
pureGavin 活跃值 2 2019-11-10 20:40
2
0
mark,楼主辛苦了
雪    币: 43
活跃值: 活跃值 (330)
能力值: ( LV7,RANK:100 )
在线值:
发帖
回帖
粉丝
GeneBlue 活跃值 2 2019-11-11 10:03
3
0
好巧,最近也看了这个文章
雪    币: 5
活跃值: 活跃值 (319)
能力值: ( LV4,RANK:40 )
在线值:
发帖
回帖
粉丝
artake 活跃值 2019-11-11 10:04
4
0
很少有这样经常的fuzzing文章,楼主辛苦了,点赞
雪    币: 1
活跃值: 活跃值 (213)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Flyour 活跃值 2019-11-12 23:04
5
0
好巧,我之前也用winafl 跑过这个ACDsee,不过好像只跑出来了十几个cnvd。
雪    币: 524
活跃值: 活跃值 (36)
能力值: ( LV9,RANK:140 )
在线值:
发帖
回帖
粉丝
jasonk龙莲 活跃值 1 2019-11-14 11:49
6
0
雪    币: 4952
活跃值: 活跃值 (648)
能力值: ( LV7,RANK:110 )
在线值:
发帖
回帖
粉丝
obabydbg 活跃值 2 2019-11-30 13:45
7
0
雪    币: 71
活跃值: 活跃值 (81)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
xaoA 活跃值 2021-3-29 17:19
8
0
学习了
游客
登录 | 注册 方可回帖
返回