如何利用Office产品绕过EDR以及防御措施
前言
如今,从进程中删除EDR的钩子已经成为一种非常普及的技术:攻击者经常利用这种技术来规避反恶意软件。防御者曾试图对抗这些攻击,但最终都以失败告终,因为他们都将主要精力放在确保恶意可执行文件无法在端点上运行方面,比如白名单或其他访问控制技术。防御方期望通过这些技术与密集的日志记录相结合,来检测这些攻击,防止任何进一步的操作,从而斩断攻击链。
不幸的是,攻击方会不断地通过发明新的方法来适应当前的防御机制。随着EDR产品开始使用Windows事件跟踪(ETW)来增强其检测能力,攻击方也开始篡改这些功能,以防止触发ETW事件。当涉及到规避访问控制时,攻击方通常依赖于可信的应用程序或无文件落地的攻击。这些类型的攻击一般难以阻止或检测,因为它们使用合法的应用程序来执行恶意操作。
本文将讨论无文件落地攻击的有效性等主题,包括它们的用例。我们还将讨论Ivy,这是一个新的payload创建框架,它利用Microsoft的OfficeVBA环境以编程方式从进程中解除EDR的钩子。然后,该框架会加载、解密和执行shellcode,同时避免被基于签名的VisualBasic应用程序(VBA)宏攻击规则所发现。Ivy技术都是基于VBA代码的无文件落地攻击(就像典型的Office宏payload一样),但是,这些攻击不会受到部署内置在Office产品中的“禁用宏功能”所制肘。在本文中,我们将详细讨论这些技术的内部工作原理,以及防御者应该如何检测这些类型的攻击。
无文件的恶意软件
所谓无文件的恶意软件技术,通常是指利用合法进程加载代码或脚本来执行恶意活动,如在法进程的内存中执行shellcode。因此,我们在磁盘上几乎找不到它们的踪迹。如今,一些老辣的攻击者会经常使用这种类型的攻击,不仅是为了防止被反恶意软件(如EDR)的发现,也是为了规避白名单机制等访问控制。
无文件恶意软件最明显的例子是PowerShell脚本攻击。在很长一段时间内,这被攻击方用作在终端上执行恶意代码而不被发现,或防止在磁盘上留下踪迹的一种方式。这主要是由于PowerShell进程能够从远程位置将PowerShell脚本导入内存,例如通过HTTP请求从托管该脚本的网站完成加载。一旦加载,模块就能被调用,脚本将运行,并在该PowerShell进程的内存环境中执行恶意代码。
图1:PowerShell无文件落地型脚本投递示意图
对于这种技术,另一个很好的例子是Casey Smith(@subTee)的研究。Casey的技术利用本地的wmic.exe二进制文件和样式表来执行内存中的shellcode。这是通过将vb sc ript嵌入样式表(.xsl)来完成的,并且通过调用组件对象模型(COM)中的对象来执行相应的动作。该技术之所以成功,是因为wmic.exe中的多个命令行参数接受“/format”选项——该选项可以检索包含嵌入的vb sc ript代码的远程样式表,并将其加载到内存中。
图2:Calc.xsl文件
图3:基于内存攻击的WMIC启动了Calc.exe
通过与其他技术(如DotNetToJsc ript)结合实现在内存中加载.Net程序集,并将其反序列化以运行,可以更进一步提高该技术的威力。这些攻击主要特点是“无文件落地的”,但当针对更高级的EDR产品(如MicrosoftDefender)进行测试时,它们会提示存在WMIC样式表攻击。
图4:WMIC Web缓存示例提交
因此,这些攻击并不是真正的无文件落地型攻击,但由于它们的特性,通常很难被EDR检出并进行取证分析。为了成功发动攻击,他们需要利用端点上已经存在的组件。现在,让我们开始考察所有使用微软办公套件的公司都需要面对的一个东西:宏,它是用VBA编写的,并保存在启用宏的Office文档中。尽管它们通常是出于合法的原因创建的,如文档的自动化处理,但攻击者也可以创建相应的宏来获得访问权限或绕过其他安全控制。
VBA与更常见的脚本子集vb sc ript(VBS)有一点不同。VBS可以创建小规模应用程序、自动化处理等,并且历史上一直用作独立的脚本,在基于Office的应用程序环境之外提供恶意软件。也就是说,VBA和基于宏的payload并不是什么新鲜事物。
宏的特征(或签名)
虽然用来执行基于宏的payload的方法五花八门,但它们大多都具有以下共同属性:
- 它们存在于支持宏的Office文档中
- 这些文件必须在磁盘(而非在内存)中才能运行
- 它们只能在“启用宏的文档”中执行
- 为了在终端上执行操作,需要调用或加载Office以外的外部资源
考虑到这些,使用一些常见的手段就可以很容易地检测并阻止它们执行,如“禁用宏功能”,就可阻止所有宏在端点上执行,并且该功能可由管理员进行设置。虽然这种方法能防御基于宏文档的payload,但对于使用VBA对象模型的恶意软件却无能为力:因为Office的VBA环境内的对象可以从外部资源中调用。
Ivy框架简介
Ivy是一个payload生成框架,可以直接在内存中执行任意的VBA(宏)源代码。Ivy的加载器通过利用VBA对象环境中的编程能力来加载、解密和执行shellcode。这种技术尽可能地接近于真正的无文件攻击,因为大多数无文件落地型攻击需要将某种形式的文件(如临时文件)放在磁盘上,从而绕过了用于检测VBA代码的基于签名的标准规则。
Ivy提供了两种不同类型的加载器来执行这些操作。第一个是Inject,它执行进程注入攻击,在挂起状态下产生一个新进程,并在进程恢复之前将Shellcode注入到该进程中。虽然注入攻击生成的是一个非Excel进程,但EDR非常善于检测为注入而创建并挂起的进程。
第二个选项是Thread,它直接将shellcode加载到当前的Excel进程中,在该进程中生成一个新的线程供shellcode运行之用。并且,这种加载方式还提供了额外的功能以避免被发现,具体来说就是直接调用某些Windows系统调用。这是由于VBA环境允许我们根据堆栈来定义和调用特定的函数(只要我们事先对齐了相应的寄存器)。最后,这种加载器还用了一个未公开的系统调用来执行shellcode,这使得它很难检测出来。
如果默认情况下禁用了宏,那么这种攻击仍然奏效吗?
是的,Ivy并不依赖于传统意义上的宏执行。为了执行shellcode,我们需要以编程方式启用“信任对Microsoft Visual Basic for Applications (VBA)项目对象模型的访问”。这个选项允许Office应用程序以编程方式访问Office的VBA环境中的对象或代码。如果没有启用这个设置,未经授权的程序在建立可能危及终端的 “自我复制”代码的时候会更加困难。因此,这种攻击不受微软Office中“禁用所有宏”选项的影响。由于没有系统管理权限的用户也可以修改它,这就意味着在端点上任意完整性级别下运行的所有用户进程都可以随意编辑这个值。
图5:宏设置
下面的代码块将为Wsc ript.Shell创建一个COM对象,以便访问Windows操作系统的shell功能。攻击者可以利用操作系统shell与Windows注册表互动。通过这种方式,加载器可以检查系统上安装的微软Office的版本,然后创建与VBA环境的可信访问相关的注册表键(或者修改该键,如果它已经存在但被设置为零的话)。这样一来,就可以对任何可以访问VBA的Microsoft Office产品都启用编程访问。
var Shell = newActiveXob ject("Wsc ript.Shell");
var strRegPath = "HKEY_CURRENT_USER\\Software\\Microsoft\\Office\\<OfficeVersion>\\Office Application>\\Security\\
Shell.RegWrite(strRegPath, 1,"REG_DWORD")
图6:宏的安全设置——允许访问VBA对象模型
几年前,微软发布了一个组策略,以帮助缓解AccessVBOM的程序性滥用。通过启用该组策略,该选项变得不可用(甚至本地管理权限也不能启用该设置)。这是微软防止恶意软件自我复制和执行恶意代码的方法。
图7:禁止用户模式对AccessVBOM的访问
但是,这并意味着无法激活该设置。通过使用另一个注册表项,我们可以启用“信任对VBA项目对象模型的访问”,从而获得相同级别的访问权限。这是通过创建“安全”文件夹的方法来实现的:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Office\<OfficeVersion>\<Office Application name>\Security
虽然这种方法是有效的,但会收到一个警告。若要修改HKEY_LOCAL_MACHINE下注册表中的任何内容,进程需要提升权限。
图8:本地计算机注册表绕过技术
一旦在端点上启用了AccessVBOM,加载器就可以通过调用“createob ject(Excel.Application)”来创建Office进程的COM实例。其中,Excel.applicationCOM对象表示整个Excel应用程序,并且表示自动化形式运行,并允许与它进行编程交互。Excel.Application的另一个重要特性是,它显然是在服务宿主进程(svchost.exe)下而不是父进程下生成的。
图9 通过Svchost.exe运行Excel
之后,加载器将生成一个隐藏的Office进程,并将加密的字符串加载到一个VBA函数。一旦进入内存,恶意代码就可以被执行了。这是通过使用ActiveX来模拟等效的GUI操作来完成的。ActiveX为Jsc ript和vb sc ript等脚本引擎提供了支持,使它们能够与Windows或第三方应用程序互动。
这就绕开了很多监控执行的传统防御措施。因此,解密函数和shellcode可以直接从一个内存缓冲区移动到另一个内存缓冲区,而无需在磁盘中落地。最后,加载器使用command-GUI调用并执行run函数,以模拟在VBA的GUI面板上点击运行宏按钮的行为。这就就会启动解密函数,并实际执行相应的shellcode。
使用VBA代码解除EDR的钩子
Ivy不仅在端点上执行shellcode的方式非常新颖,同时,它还有另外一个独特之处,即从Office VBA对象环境中解除EDR的钩子。由于EDR依赖用户模式钩子的特性,任何有效的加载器都需要执行某种类型的EDR解钩处理。由于钩子存在于用户模式中并钩入我们的进程,所以,我们可以设法控制它们。而且,由于进程是在用户的上下文中运行的,所有加载到我们进程中的代码,我们都能通过这种方加以处理。值得注意的是,一些敏感的内存区域被设置为执行、读取(ER-)权限,而没有赋予写权限,这就阻止了对这些区域的修改。我们将介绍绕过这一限制的各种方法,但如果你很好奇,想了解更多关于解除用户空间的EDR钩子的信息,请参考相关文章(EndpointDetection and Response: How Hackers Have Evolved)或框架ScareCrow(https://github.com/optiv/ScareCrow)。
虽然这些文章谈到了如何使用二进制代码和DLL来解钩和加载shellcode,但Ivy却可以直接从它运行的进程中解除EDR的钩子,或使用VBA脚本引擎从另一个进程中解钩,并且所有操作都是在内存中完成的。这使得Ivy能够使用低级别的系统调用,如在VBA中声明它自己版本的Windows函数WriteProcessMemory。
除此之外,Ivy还可以覆盖不可写的内存区域,而不需要调用任何改变内存属性的API函数。实际上,这是通过WriteProcessMemory函数完成的,该函数将内存区域的权限暂时改为可写的(也就是说,这要求我们具有足够的权限;别担心,我们的确拥有相应的权限,因为我们是这个进程的拥有者)。在写入相应的内容后,该函数将恢复内存区的原始权限,但是,它并不会调用VirtualProtect函数;相反,它自动调用相关的Syscall(NtProtectVirtualMemory函数)。
因此,Ivy不需要利用自定义Syscall来调用NtWriteVirtualMemory函数,因为WriteProcessMemory这个内置函数可以临时修改内存区域的访问属性。这已被微软列为一项功能,它能使调试器更加稳定。如果调试器想临时修改内存访问属性,那么,它们可以直接修改,而不必执行多个任务。(关于这个函数的更多信息,请参考微软的相关文章)。
Ivy不是重新加载被钩住的DLL的整个.text部分,而是针对被钩住的特定API函数。通过审查和监测以前研究的EDR钩住的地方,Ivy解除了众多系统加载的DLL中的特定API的钩子。从进程的角度来看,解除钩子的过程如下所示:
- Ivy通过VBA中的声明功能和WriteProcessMemory所需的参数和参数类型,创建一个对WriteProcessMemory API函数的引用。这个函数调用WriteProcessMemory所在的确切内存地址。(它看起来像是对寄存器RAX的调用,而不是调用kernel32.dll库的WriteProcessMemory函数)。这意味着我们没有直接调用WriteProcessMemory,但是仍然能够使用其所有的功能。
图10:在VBA中声明WriteProcessMemory
- EDR只会看到一串汇编代码,它们不匹配任何恶意指标,并指向一个内存地址。这个内存地址是一个函数的开始位置,但由于ASLR的缘故,这个函数地址是唯一的。所以,这里需要对每个函数都进行查询。
- 在执行写操作之前,系统调用ZWQueryVirtualMemory将被执行,以查看该内存区域的访问权限。如果这个内存没有被设置为可写,就会调用NtProtectVirtualMemory来修改相应的权限。
- 8字节的汇编代码被写到特定的内存地址。NtProtectVirtualMemory被再次调用以恢复原来的访问权限。这会导致汇编指令jump call(即钩子)被删除,原始值被恢复为相应的API。
图11:被挂钩后的Excel进程
图12:解除钩子后的Excel进程
Ivy能够解除EDR产品可能钩住的各种API。实际上,EDR通常并不会只钩住Ntdll.dll,所以,如果只是解除Ntdll.dll的钩子的话,EDR仍能收到遥测数据——如果在同一链中存在其他用户模式的钩子,遥测数据仍会被发送到EDR。
例如,如果你解除了Ntdll.dll中的NTAllocateVirtualMemory的钩子,而VirtualAllocate(位于Kernelba se.dll中)也被钩住了,这时,前者为了获得所需的数据,仍然需要调用后者,这意味着任何通过VirtualAllocate的东西仍然被EDR读取。一旦所有的EDR钩子都被清除掉,加载器就会执行正常的动作来执行shellcode。根据选择的是Thread还是Inject方法,Ivy将使用一个特定的函数来分配内存空间,然后再次使用WriteProcessMemory将shellcode复制到内存中,最后通过相应的API将执行控制权转移给shellcode。
图13:建立远程连接
这些技术已经按照相应的流程负责任地披露给微软安全响应中心。微软审查了我们所提供的信息,认为目前的安全控制措施足以防御这些技术。因此,我们提供了初步的防御性控制,以检测这些技术的可利用性。
OpSec注意事项
Ivy高度依赖于微软Office套件,这使得在没有安装Office的系统(如服务器)上,这个框架是难以奏效的。
使用Ivy生成文件时,您需要同时输入64位和32位的shellcode。这是因为:虽然操作系统可能是64位的,但正在运行的Office版本实际上可能是32位的。因此,Ivy在加载payload之前需要检测要使用哪种架构。
防御措施
目前,Ivy依赖于将注册表项AccessVBOM设置为“1”。如果没有这个设置,Ivy大部分情况下都会失败。Ivy尝试在执行时将正确的值写入(或创建)注册表项。默认情况下,注册表项AccessVBOM被设置为“0”(如果存在的话)。由于对于大多数进程而言,正常的执行过程无需访问这个注册表项,因此,通过检测对它的修改,可以有效发现野外类似Ivy这样的进程。
例如,以下EDR搜索将显示Ivy执行情况:
CarbonBlack:“(regmod_name:*\\Security\\AccessVBOM)”
CrowdStrike: “RegValueName=AccessVBOMRegSystemConfigValueUpdate Regob jectName="*\\Security"
对该事件发出警报(或阻止)可能有助于降低在环境中执行Ivy的风险。当然,只有在AccessVBOM注册表项通常设置为“0”时,这才会起作用,所以建议将警报与上述的组策略修复结合起来。需要注意的是,可能存在启用AccessVBOM的正常业务案例,并且应该对这些业务操作进行审查,以确保不会产生错误或误报。
如果在一个环境中怀疑存在类似Ivy的攻击活动,那么检查AccessVBOM注册表项设置是否正确也是一个很好的做法。目前,Ivy的EDR脱钩过程发生在AccessVBOM修改之后,但是可以进行相应的改动,使EDR脱钩发生在注册表修改之前。如果对AccessVBOM注册表项的定期检查时发现,当环境的其余部分被策略设置为“0”,但是系统的设置为“1”,则需要进一步进行检查。
除了直接检测Ivy的执行情况外,更重要的是要考虑该工具的使用环境,因为它只是整个链条中的一个环节。像其他通常以用户工作站为目标的恶意软件payload一样,Ivy的代码可以通过多种不同的逻辑文件格式进行传递,而不仅限于文件附件。例如,在使用这个工具之前,会有一些初始访问技术,如通过鱼叉式网络钓鱼向用户投递武器化的文件,或可能通过PowerShell命令从远程主机下载代码。因此,通过额外的系统加固措施来限制脚本的使用,或者通过AppLocker或Windows Defender等防御软件,可以进一步增强对代码执行的预防控制。
这些项目都可以在GitHub(https://github.com/optiv/Ivy)上找到。
本文由secM整理并翻译,不代表白帽汇任何观点和立场
原文地址:https://www.optiv.com/insights/source-zero/blog/defeating-edrs-office-products
最新评论