简介
通过测试未开源Windows二进制文件来深入探讨灰盒模糊测试。这种类型的模糊测试允许人们在没有访问其源代码的情况下模糊目标。为什么要进行这种类型的模糊测试?由于它需要更多的设置和高级技能,很少有人倾向于寻找漏洞/能够找到它们。因此,它扩大了寻找新的未被发现的漏洞的可能性。
为了实现这一目标,我们需要克服几个基本挑战。
1检测代码。
1找到与fuzz相关的函数。
1修改/修补二进制文件以使其可模糊。
有很多解决方案可用于在Windows二进制文件上运行模糊测试活动,但是我们将在本章中仅关注WinAFL。WinAFL提供三种类型的检测:
1通过DynamoRIO进行动态检测-动态检测是在程序运行时修改程序的指令。
1通过Syzygy进行静态检测-静态检测是在编译时修改程序的指令。
1通过英特尔PTrace进行硬件跟踪-异步记录程序控制流的硬件功能。
虽然每种方法都有其优缺点,但我们今天将重点介绍通过DynamoRIO使用动态仪器。请参阅下面WinAFL+DynamoRIO将在模糊化目标二进制文件时执行的工作流描述。
编译WinAFL
1如果您使用DynamoRIO支持进行构建,请下载并构建DynamoRIO源代码或从https://github.com/DynamoRIO/dynamorio/releases
1如果您正在使用IntelPT支持进行构建,请通过从WinAFL源目录运行git submodule update--init--regsive来获取第三方依赖项
1打开Visual Studio命令提示符(如果您想要64位构建,则打开Visual Studio x64 Win64命令提示符)。请注意,如果您要模糊处理64位目标,则需要64位winafl. dll构建,反之亦然。
1转到包含源的目录
1键入以下命令。修改DDynamoRIO_DIR标志以指向DynamoRIO cmake文件的位置(完整路径或相对于源目录)。
For a 32-bit build:
Plaintext |
For a 64-bit build:
Plaintext |
构建配置选项
支持以下cmake配置选项:
1-DDynamoRIO_DIR=…pathtoDynamoRIOcmake-需要构建winafl. dll DynamoRIO客户端
1-DINTELPT=1-启用IntelPT模式。有关详细信息,请参阅https://github.com/googleprojectzero/winafl/blob/master/readme_pt.md
1-DUSE_COLOR=1-颜色支持(Windows 10周年纪念版或更高版本)
1-DUSE_DRSYMS=1-Drsyms支持(在可用时使用符号从-target_method获取-target_offset)。在Windows 10 v1809上启用此功能已知会导致问题,尽管有解决方法,请参阅#145
找到目标
找到正确的模糊目标并不总是容易的。关键在于找到一个足够复杂、值得测试但又足够易于访问的软件,以便您了解要模糊什么以及哪些功能有趣。
一个好的策略是针对已知包含漏洞并在披露程序中具有反应性的软件,找到这样的一个好方法是查看零日倡议的网站。
在本节中,有先前披露的bug,可以让您对测试的程序及其响应能力有一个很好的广泛了解。在这里,我们看到网件和D-link产品的漏洞被披露,这个网站上有大量先前披露的漏洞,由您搜索并找到您最感兴趣的目标。
由于模糊处理复杂目标需要一些高级技能,如逆向工程、理解大型代码库等,因此我们将重点介绍我专门为本课程创建的二进制目标。它是一个易受攻击的文件读取器,它将文件作为入口,将其内容复制到缓冲区中,然后关闭文件。
你可以在这里下载文件,密码:"bushido"https://drive.google.com/file/d/1c-cOuzYbC-gOFW91a2EHKNpZTiPrVdBP/view?usp=sharing
修补二进制文件以允许模糊测试fuzz
不幸的是,许多软件使用某种对话框控制流,提示用户在执行某个任务之前回答问题,例如“此文件已存在。你想覆盖它吗?”等。
这使得模糊测试过程变得不可能,因为它需要用户与对话框交互,这将阻止模糊程序正常运行。这就是为什么我们现在正在研究如何改进/修补二进制文件以使其可模糊化的原因!
下载并安装Ghidra,启动应用程序,然后创建一个项目目录和项目。导入vulnerable_reader. exe单击“选项…”并启用“从磁盘加载本地库”
加载库后,您可以通过按回车键或双击文件名来开始反转过程,它将提示一个对话框“分析”,您可以对其进行配置。
对于这个练习,无需更改,但我邀请您探索可用选项及其容量。点击确定后,您将看到二进制显示的反汇编代码,您需要等待Ghidra分析整个二进制文件,您可以在屏幕右下角找到进度条。
如果你在分析后保存你的程序,你将来就不需要再次分析它了。这个二进制文件很小,但请记住,情况并非总是如此。我鼓励你在分析执行后将分析后的程序保存为副本。
现在分析已经完成,我们可以通过软件看到。调查这个二进制文件会很容易,因为我们已经知道对话框中使用的一个字符串,让我们打开搜索>程序文本,然后输入“您单击了是!”在“字段”中启用“所有字段”,在块中启用“所有块”,然后单击“搜索全部”,双击结果中的第一个发现。
我们可以看到有两个可能的选项,一个是该函数允许您选择是并关闭,另一个是否并关闭。该函数没有真正的用途,但是它会阻止程序在您单击之前继续运行,从而阻止您对其进行模糊处理。
一个有趣的信息是XREFS,它对应于调用这个函数(FUN_00401000)的位置。在这里,我们可以看到这个函数是由FUN_00401130调用的,让我们双击看看这个函数是什么。
看起来这个函数基本上是我们的主要函数。它接受两个参数作为参数并将其传递给第二个函数。第一个函数是负责对话框的函数。
让我们用NOP指令替换指令“CALLFUN_00401000”
如您所见,现在有一堆“??”跟随我们的指令。这是因为初始指令比NOP指令大(十六进制:90),所以我们也需要用NOP指令替换“??”以尊重填充。更多信息https://en.wikipedia.org/wiki/Data_structure_alignment
结果必须如下所示:
现在将程序导出为PE文件,单击文件>导出程序,然后选择原始文件并放置正确的路径:
让我们运行程序,看看对话框是否再次发生:
太棒了!请记住,大多数程序都需要更复杂的交互,这门课程不是关于逆向工程的。然而,运行成功的模糊测试活动的一个重要方面是消除使模糊测试变慢的因素,GUI是其中的一个重要部分。如果你想从事模糊测试的研究,你绝对应该对RE感兴趣。
函数偏移
WinAFL使用一种技术来优化模糊测试过程,通过减轻与执行系统调用和典型进程启动过程相关的缓慢执行时间。它不是为每次模糊测试尝试重新初始化目标程序,而是采用受fork服务器概念启发的策略。
基本思想是通过提供随机输入来执行程序直到到达所需的模糊点。通过采用这种方法,每个子进程只处理单个输入,有效地规避了与精确系统调用操作相关的开销。
因此,当使用WinAFL对程序进行模糊测试时,如果在第三次调用期间达到所需的模糊测试点,则性能不受影响。然而,显着的优势在于减少了整个程序中模糊测试的开销,从而导致更高效和有效的模糊测试会话。
这是一个说明这个过程的图表。
如何选择目标函数
目标函数应该在其生命周期内执行以下操作:
1打开输入文件。这需要在目标函数中发生,以便您可以在每次迭代中读取一个新的输入文件,因为输入文件在目标函数运行之间被重写。
1解析它(这样可以衡量文件解析的覆盖率)
1关闭输入文件。这很重要,因为如果输入文件没有关闭,WinAFL将无法重写它。
1正常返回(以便WinAFL可以“捕获”此返回并重定向执行。通过ExitProcess()“返回”等不起作用)
如何找到函数的虚拟偏移量
1使用Ghidra和radare2等工具进行静态分析
1使用WinDBG或x64dbg调试代码(设置断点并在运行时分析函数的参数)
1使用辅助工具,如API监视器、过程监视器和覆盖率工具,如ProcMon
通过使用Ghidra进行静态分析查找偏移量
二进制文件包含一些字符串,其中之一是“打开文件失败”,让我们点击搜索菜单,然后点击“程序文本”,寻找这句话:
让我们点击搜索全部并检查结果:
让我们双击命名空间中的第一个匹配项FUN_00401060
记住我们要找的执行流程是:打开文件>读取它>关闭文件>返回正常执行。让我们调查一下这个流程是否发生在函数的伪代码中。简化它给我们:
Plaintext localVariable = DAT_0041c040 ^ (uint)&stack0xfffffffc; if (argc < 2) { |
听起来很匹配!现在让我们找到这个函数的偏移量。很简单,让我们右键单击函数并显示字节。我们看到函数的地址是0x00401060,基地址是0x0040000,所以函数偏移量是0x01060
Ghidra CheatSheet:https://ghidra-sre.org/CheatSheet.html
准备模糊测试fuzz环境
模糊二进制文件是一项相当耗费资源的任务,以下是您可以做的一些事情,以准备您的环境以顺利运行模糊测试活动:
1禁用自动调试
1禁用AV扫描
WinAFL优化
拥有良好的输入语料库是模糊测试的一个非常重要的方面。WinAFL提供了两个选项来优化您的c-min.py语料库。使用示例:
1典型使用
winafl-cmin.py-D:DRIObin32-t 100000-i in-o minset-covtype edge-coverage_modulem. dll-target_moduletest.exe-target_methodfuzz-nargs 2-test.exe@@
1运行:
winafl-cmin.py-C-dry-run-w 4-working-dir D:dir-D D:DRIObin32-t 10000-i in-i C:fuzzin-oout_mini-covtype edge-coverage_modulem. dll-target_moduletest.exe-target_methodfuzz-nargs 2-test.exe@@
1从特定文件读取
winafl-cmin.py-D:DRIObin32-t 100000-i in-o minset-f foo. ext-covtype edge-coverage_modulem.dll-target_moduletest.exe-target_methodfuzz-nargs 2-test.exe@@
1用模式从特定文件读取
winafl-cmin.py-D:DRIObin32-t 100000-i in-o minset-f prefix-@@-foo. ext-covtype edge-coverage_modulem.dll-target_moduletest.exe-target_methodfuzz-nargs 2-test.exe@@
1静态仪器的典型用途
winafl-cmin.py-Y-t 100000-i in-o minset-test. exe@@
winafl-cmin.py可能需要一段时间才能跑步,所以要有耐心。
开始动工
我们已经修补了二进制文件以使其可模糊化,找到了我们想要测试的函数的偏移量,现在让我们玩得开心并运行模糊器!WinAFL提供了不同的选项,让我们枚举它们:
1t-每次模糊测试迭代超时。如果未完成WinAFL,请重新启动程序;
1D-DynamoRIO路径
1coverage_module-记录覆盖范围的模块。
1target_module-目标功能模块。
1target_offset-从模块开始模糊处理的函数的虚拟偏移量;
1fuzz_iterations-在重新启动程序的exec之前进行模糊迭代。
1call_convention-指定调用传输:sdtcall,cdecl和thiscall。
1nargs-模糊化函数接受的参数数。this指针(在thiscall 调用约定中使用)也被视为参数。
警告:记住,使用正确的AFL版本的目标,你要模糊fuzzing!在这里,我们将使用32位版本!
由于我们的二进制文件旨在打开和读取文本文件,因此创建一个“in”文件夹并将带有简单短语的文本文件作为内容。
好的,现在让我们cd到WinAFL_32构建目录,并运行以下命令:
Plaintext |
如果一切顺利,你应该会看到这个美丽的出现:
现在只是时间问题。让模糊器运行几分钟,然后您应该会看到崩溃出现。
分析crash测试
在这里,WinAFL很快就发现了一个崩溃。我故意设计了一个非常简单的二进制崩溃,以便本教程变得有趣。正如您所看到的,WinAFL以崩溃的状态和类型命名崩溃文件。您可以在您的out目录中找到它们>崩溃
这显然是一个Stack BoF,因为该程序是专门为此设计的。但是,让我们在WinDBG中打开它并对崩溃进行根本原因分析。
启动WinDBG并单击文件>启动可执行文件(高级),然后将易受攻击的二进制文件的路径设置为可执行文件,将crash_id文件设置为参数,然后单击转到运行程序。WinDBG立即检测到堆栈缓冲区溢出
原文始发于微信公众号(暴暴的皮卡丘):模糊测试Windows二进制文件
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论