当前位置: 首页 > 科技观察

实战案例:如何使用Speakeasy轻松模拟恶意Shellcode

时间:2023-03-16 11:45:02 科技观察

一、概述为了大规模模拟恶意软件样本,研究人员最近开发了Speakeasy模拟框架。该框架使非恶意软件分析师的用户能够轻松自动获取分类报告,同时还帮助逆向工程师为难以分类的恶意软件家族编写自定义插件。Speakeasy最初是为了模拟Windows内核模式恶意软件而创建的,现在也支持用户模式样本。该项目的主要目标是对Windows操作系统进行高级仿真,以便在x86和amd64平台上进行动态恶意软件分析。我们知道目前有类似的模拟框架可以模拟用户态二进制文件,但是Speakeasy的特点包括:1.专门为模拟Windows恶意软件而设计;2、支持模拟内核态二进制文件,从而可以分析难以归类的Rootkit;3.结合当前恶意软件趋势进行模拟和API支持,提供一种无需额外工具即可提取威胁指标的方法;4.提供完全可配置的仿真环境,无需额外代码。该项目目前支持内核模式驱动程序、用户模式??WindowsDLL和可执行文件、shellcode。可以自动模拟恶意软件样本并生成报告供分析人员进行后处理。我们的下一个目标是继续增加对新的或常见的恶意软件系列的支持。在这篇文章中,我们将展示一个案例研究,说明Speakeasy如何有效地从在线恶意软件集合中获得的CobaltStrikeBeacon样本中自动提取威胁指标。二。背景Windows恶意软件的动态分析一直是恶意软件分析过程中的关键步骤。了解恶意软件如何与WindowsAPI交互并提取有价值的主机和网络妥协指标(IoC)对于评估恶意软件对网络的实际影响至关重要。通常,研究人员使用自动化或有针对性的方法进行动态分析。示例包括在沙箱中按顺序运行恶意软件以监控其功能,或手动调试以识别在沙箱运行期间未能执行的代码路径。从历史上看,代码模拟已用于测试、验证甚至恶意软件分析。能够模拟恶意软件代码有利于手动和自动分析。通过对CPU指令的模拟,可以充分检测到二进制代码,并且在这个过程中,控制流可以最大程度地覆盖代码。在模拟过程中,可以监控和记录所有功能,以快速提取威胁指标或其他有用的情报。与在虚拟机沙箱中执行相比,模拟具有一些优势,其中一个关键优势是减少了环境中的噪音(误报)。在模拟期间,仅记录恶意软件作者编写的或静态编译为二进制文件的活动。虚拟机(尤其是内核模式)中的API挂钩很难归因于恶意软件本身。例如,沙盒解决方案通常会挂钩堆分配器API调用,但它们通常无法判断恶意软件作者是否打算分配内存,或者是否由较低级别的API负责内存分配。然而,模拟方法也有缺点。由于我们在分析阶段脱离了操作系统,因此仿真器负责提供API调用的预期输入和输出以及模拟期间发生的内存访问。要成功模拟在普通Windows系统上运行的恶意软件样本需要付出很多努力。3.Shellcode攻击方法分析通常,Shellcode是攻击者在受感染系统上隐身的绝佳选择。Shellcode在可执行内存中运行,不依赖于磁盘上的任何文件。这使得攻击者的代码可以轻松地隐藏在大多数形式的传统取证无法识别的内存中。必须首先识别加载shellcode的原始二进制文件,否则必须从内存中转储shellcode本身。为了逃避检测,攻击者可以将shellcode隐藏在合法的加载程序中,并将其注入另一个用户模式进程。在文章的第一部分,我们将介绍一种在应急响应中遇到的比较常见的shellcode恶意软件样本,并分析如何模拟它。CobaltStrike是一个商业渗透测试框架,通常使用分阶段程序来执行其他代码。这些阶段之一往往是通过HTTP请求下载其他代码并执行HTTP响应的数据内容。在这种情况下,此响应的数据成为shellcode,它通常会解码内容以获得有效的PE,其中包含反射加载自身的代码。在CobaltStrike框架中,这种情况下的有效载荷通常是一个名为Beacon的植入物。Beacon被设计为内存驻留后门,用于在受感染的Windows系统上维护命令和控制(C2)。由于它是使用CobaltStrike框架构建的,因此无需任何代码级修改即可轻松调整其核心功能和命令与控制信息。上述这些特性使攻击者能够在受感染的网络中快速构建和部署Beacon注入工具的新变种。因此,我们需要一个工具来快速提取Beacon变体组件,理想情况下,最好不要占用恶意软件分析人员太多宝贵的时间。4、Speakeasy原理分析Speakeasy目前使用基于QEMU的模拟器引擎Unicorn来模拟x86和amd64架构的CPU指令。Speakeasy希望未来通过抽象层支持任何模拟引擎,但目前仍然依赖Unicorn。如果要借助沙箱分析所有样本,可能需要使用完整的操作系统沙箱来分析完整样本。在这种情况下,很难模拟所有版本的Windows系统。沙箱可能难以根据需要进行扩展,并且运行样本可能会花费大量时间。但是,如果我们能够识别特定的恶意软件系列(例如本例中的Beacon),我们就可以大大减少需要进行逆向工程的变体数量。在分析恶意软件变体期间,希望能够以自动方式生成高级分类报告。这使恶意软件分析师有更多时间专注于可能需要更深入分析的样本。使用Speakeasy时,Shellcode或WindowsPE会加载到模拟地址空间中。在尝试模拟恶意软件之前,我们首先创建了Windows内核模式和用户模式的基本模拟所需的Windows数据结构。伪造进程、驱动程序、设备和用户模式库,为恶意软件提供近乎真实的执行环境。恶意软件可以与模拟文件系统、网络和注册表功能交互。所有这些模拟子系统都可以使用单独的配置文件进行配置。WindowsAPI由PythonAPI处理程序处理。这些处理程序将尝试模拟这些API的预期输出,以便恶意软件样本继续预期的执行路径。在定义API处理程序时,我们只需要API的名称、API期望的参数数量以及可选的调用约定规范。如果未提供调用约定,则假定为stdcall。在当前版本中,如果尝试进行不受支持的API调用,Speakeasy将记录不受支持的API并继续到下一个入口点。下图显示了kernel32.dll导出的WindowsHeapAlloc函数的示例处理程序。默认情况下,所有入口点都是模拟的。例如,对于DLL,所有导出都是模拟的;对于驱动程序,IRP主要功能是单独模拟的。此外,还会跟踪在运行时发现的动态入口点。其中一些动态入口点包括创建的线程或注册的回调。在尝试确定恶意软件的影响时,将活动与特定入口点相关联对于全局至关重要。5.导出报告目前,模拟工具捕获的所有事件都会以JSON格式记录在报告中,以供后续处理。该报告包含模拟期间记录的关键事件。与大多数仿真工具一样,所有WindowsAPI调用及其参数都会被记录下来。所有入口点都被模拟并标有相应的API列表。除了API跟踪之外,还会记录其他特定事件,包括文件、注册表和网络访问。所有解码或内存中的字符串都被转储并显示在报告中,揭示了通过静态字符串分析未找到的关键信息。下图显示了Speakeasy的JSON报告中记录的文件读取事件示例:6.运行速度由于该框架是用Python编写的,因此速度是一个明显需要关注的维度。Unicorn和QEMU是用C编写的,提供非常快速的仿真。但是,我们用Python编写了API和事件处理程序。本机代码和Python之间的转换过程需要一些时间,应该尽量减少。因此,我们的目标是仅在绝对必要时才执行Python代码。默认情况下,我们在Python中处理的唯一事件是内存访问异常或WindowsAPI调用。为了捕获WindowsAPI调用并在Python中模拟它们,导入表填充了无效的内存地址,因此我们只有在访问导入表时才切换到Python。当shellcode访问加载在恶意软件的模拟地址空间中的DLL的导出表时,会使用类似的技术。通过尽可能少地执行Python代码,我们可以以合理的速度控制框架,同时允许用户开发框架的功能。7.内存管理Speakeasy在仿真工具引擎的内存管理上实现了轻量化。通过跟踪和标记恶意软件分配的每个内存块,可以获得有意义的内存转储。对于分析人员来说,能够将恶意活动与特定内存块相关联将非常有帮助。通过记录对敏感数据结构的内存读写,它可以揭示API调用记录可能无法揭示的恶意软件的真实意图,这对Rootkit样本特别有用。Speakeasy提供了一个可选的“内存跟踪”功能,该功能将记录所有内存访问样本触摸。记录所有内存读取、写入和执行。由于模拟工具标记了所有分配的内存块,因此可以从该数据中收集更多上下文。如果恶意软件hook关键数据结构,或者将执行转移到动态映射的内存中,那么这个进程就可以有效地发现它,这对调试或分析很有帮助。但是,此功能会占用大量资源,默认情况下处于关闭状态。提供给恶意软件的模拟环境包括通用数据结构,shellcode可以在其中找到并执行导出的Windows系统功能。为了调用Win32API,必须解析导出的函数,以便对目标系统产生有意义的影响。在大多数情况下(包括Beacon),这些功能是通过遍历进程环境块(PEB)来定位的。Shellcode可以从PEB访问进程虚拟地址空间中所有已加载模块的列表。下图是模拟BeaconShellcode样本生成的内存报告。在这里我们可以看到恶意软件遍历PEB以找到kernel32.dll的地址。然后恶意软件手动解析并调用VirtualAllocAPI的函数指针,然后继续解码,将自身复制到新的缓冲区中以控制执行。内存跟踪报告:8.配置Speakeasy具有高度可配置性,允许用户创建自己的配置文件。除此之外,还可以指定不同程度的分析来优化各个用例。最终目标是让用户无需调整代码即可轻松切换配置选项。目前,配置文件的结构是一个JSON文件。如果用户没有提供配置文件,则使用框架的默认配置。Brother字段记录在Speakeasy的项目文档中。下图显示了网络模拟工具配置部分的代码片段。在这里,用户可以指定在进行DNS查找时返回哪些IP地址,或者在某些Beacon样本的情况下,在TXT记录查找期间返回哪些二进制数据。同样,也可以自定义HTTP响应。网络配置:许多恶意软件在HTTP阶段使用HTTPGET请求来检索Web资源。比如CobaltStrike或者Metasploit会立即执行这个阶段的响应内容,以继续下一阶段。可以使用Speakeasy的配置文件轻松配置响应的内容。在上面显示的配置中,除非被覆盖,否则框架会使用default.bin文件中包含的数据进行响应。该文件当前包含调试中断指令(int3),因此如果恶意软件尝试执行数据,它将退出并记录在报告中。使用此工具,我们可以轻松地将恶意软件归类为“可以下载其他代码”的下载程序。同样,也可以配置文件和注册表路径,将返回的数据变成你想在Windows系统上运行的样本。9.局限性如前所述,模拟方法也带来了一些挑战。如何与被模拟的系统保持一致的功能对我们来说是一场持续的战斗。然而,这也提供了一个独特的机会来控制恶意软件并包含更多功能。在模拟未完全完成的情况下,仍然可以生成模拟报告和内存转储以收集尽可能多的数据。例如,后门可能会成功安装其持久性机制,但无法连接到C2服务器。在这种情况下,仍然可以记录主机级别的指标,为分析师提供价值。同时,可以轻松地将缺少的API处理程序添加到模拟工具中以处理这些情况。对于许多API处理程序,只需返回成功代码就足以让恶意软件继续执行。虽然不可能完全模拟每个恶意软件,但针对特定恶意软件家族的能力可以大大减少对同一家族的变体进行逆向工程的需要。10.Speakeasy的使用方法目前发布在GitHub上。可以使用项目中的Python安装程序脚本进行安装,也可以使用提供的Dockerfile安装在Docker容器中。该框架与平台无关,可以在Windows、Linux或macOS中模拟Windows恶意软件。有关详细信息,请参阅项目的自述文件。安装后,Speakeasy可用作独立库或使用提供的run_speakeasy.py脚本直接调用。在本文中,我们将演示如何从命令行模拟恶意软件样本。有关如何将Speakeasy用作库的信息,请参阅项目的README文件。包含的脚本模拟单个样本并根据记录的事件生成JSON报告。下图是run_speakeasy.py的命令行参数。Speakeasy还提供了丰富的开发和Hooking接口,用于编写自定义插件。11、实战:Beacon注入工具的模拟流程本例中,我们将模拟Shellcode,解码后执行Beacon植入变体。我们首先验证样本的文本格式。此示例应由加载程序启动,或作为漏洞利用负载的一部分。恶意软件十六进制转储示例:从上图中,我们可以清楚地看到该文件不是PE文件格式。有shellcode样本分析经验的分析师可能会注意到前两个字节-0xfc0xe8。这些字节可以反汇编成Intel的汇编指令cld和call。cld指令通常是独立shellcode的前奏,因为它会清除方向标记,使恶意软件可以轻松分析系统DLL导出表中的字符串数据。Shellcode通常使用以下call指令,通过在其后跟一个pop指令来获取当前程序计数器。这样,恶意软件就可以从内存中获取其执行位置。由于我们确定样本是shellcode,因此我们使用命令行调用Speakeasy,如下图所示。使用命令行模拟恶意软件样本:此命令将指示Speakeasy将偏移量0处的样本模拟为x86shellcode。请注意,即使我们正在模拟代码而不是实际执行它,它们仍然是攻击者生成的二进制文件。因此,如果要使用本地CPU模拟引擎,最好在虚拟机中模拟恶意代码。模拟完成后,会生成一个名为report.json的报告。此外,模拟环境的完整内存转储被压缩并写入memory_dump.zip。恶意软件将被加载到一个虚假容器进程内的模拟内存中,以模拟shellcode所期望的真实执行环境。模拟开始后,模拟的API调用及其参数和返回值将被记录到屏幕上。下图显示了Beacon示例,它分配了一个新的内存缓冲区,它将在其中复制自身。然后恶意软件开始手动解析它需要执行的导出。网络配置:经过额外的解码和设置后,恶意软件会尝试连接到其C2服务器。在下图中,我们可以看到恶意软件使用Wininet库通过HTTP连接C2服务器并从中读取数据。用于连接到C2的WininetAPI调用:恶意软件将无限循环,直到从C2服务器接收到预期数据。Speakeasy将在预定时间后超时并生成JSON报告。NetworkC2Events:在生成的报告的“network_events”和“traffic”部分,聚合了网络指标。在上图中,我们可以看到IP地址、端口号以及与恶意软件建立的连接相关的HTTP标头。对于此样本,当我们模拟样本时,我们指示Speakeasy创建模拟地址空间的内存转储。这将为每个内存分配和周围上下文创建一个ZIP存档,其中包括基地址、大小和由模拟工具分配的标记,用于识别内存分配对应的内容。下图显示了模拟期间创建的内存转储文件的摘要。文件名包含与每个内存分配关联的标签和基地址。模拟得到的单块内存:如果你只是在这些内存转储上运行字符串,你可以很快找到值得注意的字符串和Beacon配置数据,如下图所示。恶意软件配置字符串数据:在分类分析中,我们可能只关心已知恶意软件变种家族的威胁指标。但是,如果需要对样本进行完全逆向工程,我们也可以以DLL形式恢复Beacon恶意软件的解码版本。通过简单地对MZ魔术字节执行原始grep,我们得到的唯一结果是与原始样本分配相关的内存转储,以及恶意软件将自身复制到的虚拟分配缓冲区。包含解码恶意软件的内存转储:如果我们查看原始shellcode缓冲区中的字节,我们可以看到它在被复制之前已经被解码并且在内存中准备好从偏移量0x48开始转储。我们现在可以成功地将解码后的BeaconDLL加载到IDAPro中进行全面分析。解码后的恶意软件已成功加载到IDAPro:XII。总结在本文中,我们展示了如何使用Speakeasy模拟框架自动对Beacon恶意软件样本进行分类。我们用它来发现有价值的网络指标,从内存中提取配置信息,并获得解码的BeaconDLL以供进一步分析。本文翻译自:https://www.fireeye.com/blog/threat-research/2020/08/emulation-of-malicious-shellcode-with-speakeasy.html如有转载请注明原文地址