简介及目标
AMSI 为终端安全服务提供商提供了一个非常棒的接口,能够让提供商从他们所选择的内容扫描的组件中深入了解内存缓冲区。 微软提供了以下组件列表,这些组件选择使用 AMSI:
· 用户帐户控制或 UAC (EXE、 COM、 MSI 或 ActiveX 安装时的权限提升)
· Powershell (脚本、交互式使用和动态代码权限提升)
· Windows Script Host (wscript.exe and cscript.exe)
· JavaScript 和 VBScript
· Office VBA 宏
作为一名从事侦查工程的防御者和一名对成熟的规避技术感兴趣的攻击研究者,我脑海里有了下面这些问题:
· 选择使用 AMSI 的这些组件的实际 PE 文件是什么?
· 文档是否准确,或者上面列表中是否有缺少的组件?
· 有没有可能不需要注册一个 AMSI 供应商作为一个终端安全的供应商就可以选择使用 AMSI?
AMSI 组件枚举
为了解决前两个问题,我们需要确定一种自动发现 AMSI 组件的方法。 所涉及的策略包括列出任何包含“amsi.DLL” 的 ASCII 或 Unicode 字符串的 EXE 或 DLL 文件。 为什么要搜索术语“amsi.dll” ?
· amsi.dll 是扫描缓冲区域所需的导出函数的DLL 文件,函数名称是: AmsiScanBuffer, AmsiScanString以及 AMSIUacScan
· 如果某个文件包含了此术语字符串则意味着 EXE 或 DLL 将通过静态导入加载 amsi.dll (即暗示它将作为 ASCII 字符串存储在 PE 中)或在运行时动态加载(即意味着它将作为 Unicode 字符串存储在 PE 中)。
下面这段粗糙的 PowerShell 代码将为我们提供答案:
$UserPEs = Get-CimInstance -ClassName CIM_DataFile -Filter 'Drive = "C:" and (Extension = "exe" or Extension = "dll")' -Property 'Name' | Select -ExpandProperty Name $AMSIReferences1 = $UserPEs | % { Select-String -Encoding ascii -LiteralPath $_ -Pattern 'amsi\.dll' } $AMSIReferences2 = $UserPEs | % { Select-String -Encoding unicode -LiteralPath $_ -Pattern 'amsi\.dll' } $AMSIReferences1.Path $AMSIReferences2.Path
上面的 PowerShell 代码片段使用 WMI 枚举所有 exe 和 dll。 之所以选择此方法是因为 Get-ChildItem 在试图访问它无法访问的文件时有抛出终止异常的倾向。 接下来,它使用 Select-String (即 PowerShell 中的 grep 的等价形式)扫描每个文件中的 ASCII 和 Unicode 的文本字符串“amsi.dll”(作为正则表达式匹配)。
在剔除结果后,得到了以下 AMSI 组件:
· %windir%\System32\consent.exe
· %windir%\System32\jscript.dll
· %windir%\System32\vbscript.dll
· %windir%\System32\wbem\fastprox.dll
· %windir%\Microsoft.NET\assembly\GAC_MSIL\System.Management.Automation\v4.0_3.0.0.0__31bf3856ad364e35\System.Management.Automation.dll
· %windir%\Microsoft.NET\Framework64\v4.0.30319\clr.dll
· %ProgramFiles%\WindowsApps\Microsoft.Office.Desktop_16051.11929.20300.0_x86__8wekyb3d8bbwe\VFS\ProgramFilesCommonX86\Microsoft Shared\VBA\VBA7.1\VBE7.DLL
通过一些推断和文件调查,微软记录的上述组件的 AMSI 组件组分类如下:
· User Account Control: consent.exe
· PowerShell: System.Management.Automation.dll
· JavaScript 和 VBScript: jscript.dll, vbscript.dll
· Office VBA 宏: VBE7.dll
· 未归类的组件: clr.dll, fastprox.dll
那么,那些未归类的 AMSI 组件又是怎么回事呢? 至于 clr.dll表示的是微软通用语言运行时,微软确实在 .Net 4.8 中提到过这点,他们用于扫描内存中的程序集加载。 在这一点上,一些研究人员已经研究了绕过方法,他们的研究工作在这里着重强调一下,如下:
· 红队如何在.NET 动态代码中绕过 AMSI 和 WLDP
· Donut v0.9.1 “Apple Fritter” — 双模式 Shellcode, AMSI 和 MoreDonut v0.9.1
这样就只剩下 fastprox.dll 了,对于这个组件的分析我们将在下文详述。
用于 WMI 的 AMSI
考虑到这个文件存在于 System32\wbem 目录中,以及 fastprox.dll 的文件描述是“ WMI 自定义信号员” ,不言而喻,这个文件与 WMI 有关。 为了进一步验证这一点,我们可以使用 PowerShell 来识别加载 fastprox.dll 的过程:
> Get-Process | Where-Object { $_.Modules.ModuleName -contains 'fastprox.dll' } Handles NPM(K) PM(K) WS(K) CPU(s) Id SI ProcessName ------- ------ ----- ----- ------ -- -- ----------- 2196 274 219988 232044 14,573.92 1192 5 chrome 1162 47 85544 38524 803.86 14580 5 mmc 692 42 129920 55564 1,081.20 2408 5 powershell 874 47 77144 87852 73.48 4040 5 powershell 686 39 71132 42608 42.78 12620 5 powershell 229 13 2596 10072 0.13 2956 0 svchost 480 20 3840 6728 69.66 3376 0 svchost 613 34 26776 17356 4,370.64 3648 0 svchost 217 43 2572 4148 18.64 6728 0 svchost 564 33 11276 16544 4.34 11520 0 svchost 129 7 1496 2196 0.77 5232 0 unsecapp 1650 67 318004 256536 99.28 16576 5 vmconnect 898 29 62664 23660 1,267.36 4776 0 vmms 386 16 8492 13408 21.77 14220 0 WmiPrvSE 176 10 2684 8592 1.36 15772 0 WmiPrvSE
可以使用以下 PowerShell 单行代码将 svchost.exe 进程解析为它们各自的服务:
> Get-Process | Where-Object { $_.Modules.ModuleName -contains 'fastprox.dll' -and $_.ProcessName -eq 'svchost' } | ForEach-Object { Get-CimInstance -ClassName Win32_Service -Filter "ProcessId = $($_.Id)" } | Format-Table -AutoSize ProcessId Name StartMode State Status ExitCode --------- ---- --------- ----- ------ -------- 2956 Netman Manual Running OK 0 3376 iphlpsvc Auto Running OK 0 3648 Winmgmt Auto Running OK 0 6728 SharedAccess Manual Running OK 0 11520 BITS Auto Running OK 0
因此,似乎任何可能希望与 WMI 交互的进程都需要与此 DLL 进行接口。 现在让我们直接看看代码,以确定 fastprox.dll 与 amsi.dll 之间的交互方式。
对“ amsi.dll”的唯一引用位于 JAMSI: : JAMSIInitialize 函数中。 以下是相关反汇编代码的一部分:
首先,如果当前进程不是%windir%\System32\wbem\wmiprvse.exe,则此函数仅初始化 AMSI。 我可以想象这样做的基本原理是降低噪音,或许是打算主要捕获远程 WMI 操作。 然而,这是不科学的推测。
下面是对 amsi.dll 上的 LoadLibrary 的调用以及相关导出函数(如 AMSIScanBuffer)的解析。 随后调用 AMSIScanBuffer 的唯一交叉引用是 JAMSI: : JAMSIRunScanner 函数:
JAMSI::JAMSIProcessor 调用了 JAMSIRunScanner,JAMSI::JAMSIProcessor 由以下函数调用:
· CWbemSvcWrapper::XWbemServices::ExecNotificationQueryAsync
· CWbemSvcWrapper::XWbemServices::CreateInstanceEnum
· CWbemSvcWrapper::XWbemServices::ExecQueryAsync
· CWbemSvcWrapper::XWbemServices::ExecQuery
· CWbemSvcWrapper::XWbemServices::CreateInstanceEnumAsync
· CWbemSvcWrapper::XWbemServices::GetObjectW
· CWbemSvcWrapper::XWbemServices::ExecMethod
· CWbemSvcWrapper::XWbemServices::ExecMethodAsync
· CWbemSvcWrapper::XWbemServices::ExecNotificationQuery
· CWbemSvcWrapper::XWbemServices::GetObjectAsync
· JAmsi::JAmsiProcessor (由 CWbemInstance::SetPropValue 调用)
除了最后一个函数之外,这些函数都对应于 IWbemServices interface 接口中实现的方法。 最后一个函数很可能对应于 IWbemClassObject::Put 方法。
现在,我们怎样才能了解到终端安全产品可能了解到的 WMI 事件数据? 我在 Palantir 发表的关于 ETW 的文章中,描述了如何将 AMSIScanBuffer 插装到 Microsoft-Antimalware-Scan-Interface 提供程序中来记录所有事件。 在 AMSIScanBuffer 中实现插装的好处在于,不管选择 AMSI 的 Windows 组件是什么,我们都有机会从任何调用 AMSIScanBuffer 的进程中跟踪所有 AMSI 缓冲区。 如果你想了解更多关于 ETW 机制和跟踪的知识,我鼓励你阅读这篇文章。 现在,让我们运行 logman 来捕获所有 AMSI 事件,然后捕获相关的 WMI 事件:
logman start trace AMSITrace -p Microsoft-Antimalware-Scan-Interface (Event1) -o amsi.etl -ets
启动跟踪后,使用 WMI 接口并查看是否生成了任何事件。 我做了以下测试:
$CimSession = New-CimSession -ComputerName . Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{CommandLine = 'notepad.exe'} -CimSession $CIMSession $CIMSession | Remove-CimSession
在上面的例子中,我创建了一个本地 CIM 会话来模拟远程 WMI 连接,在这个过程中,我可能会避免在 wmiprvse.exe 的上下文中进行跟踪,而上面描述的 wmiprvse.exe 被排除在 AMSI 之外。
最后,在完成与 WMI 的交互之后,停止跟踪:
logman stop AMSITrace -ets
然后我使用 PowerShell 尝试识别任何 WMI 事件,果然,我找到了一个!
> $AMSIEvents = Get-WinEvent -Path .\amsi.etl -Oldest > $AMSIEvents[5] | Format-List * Message : AMSIScanBuffer Id : 1101 Version : 0 Qualifiers : Level : 4 Task : 0 Opcode : 0 Keywords : -9223372036854775807 RecordId : 5 ProviderName : Microsoft-Antimalware-Scan-Interface ProviderId : 2a576b87-09a7-520e-c21a-4942f0271d67 LogName : ProcessId : 7184 ThreadId : 8708 MachineName : COMPY486 UserId : TimeCreated : 10/3/2019 12:14:51 PM ActivityId : 95823c06-72e6-0000-a133-8395e672d501 RelatedActivityId : ContainerLog : c:\users\testuser\desktop\amsi.etl MatchedQueryIds : {} Bookmark : System.Diagnostics.Eventing.Reader.EventBookmark LevelDisplayName : Information OpcodeDisplayName : Info TaskDisplayName : KeywordsDisplayNames : {} Properties : {System.Diagnostics.Eventing.Reader.EventProperty, System.Diagnostics.Eventing.Reader.EventProperty...} > $AMSIEvents[5].Properties Value ----- 0 1 1 WMI 300 300 {67, 0, 73, 0...} {131, 136, 119, 209...} False > [Text.Encoding]::Unicode.GetString($AMSIEvents[5].Properties[7].Value) CIM_RegisteredSpecification.CreateInstanceEnum(); Win32_Process.GetObjectAsync(); Win32_Process.GetObject(); SetPropValue.CommandLine("notepad.exe"); > Get-CimInstance -ClassName Win32_Service -Filter "ProcessId = $($AMSIEvents[5].ProcessId)" ProcessId Name StartMode State Status ExitCode --------- ---- --------- ----- ------ -------- 7184 WinRM Auto Running OK 0
好吧,那么我们该如何理解这一切呢? 首先,在处理每个跟踪事件时,第六个事件(索引5)是第四个 Properties 值包含“ WMI”的唯一事件。 另外,第八个 Properties 值包含了看起来像是由 Unicode 字符串组成的二进制数据。 在解码时,它证明了我在上面举的例子中提及的 Win32_Process 创建的执行。 还有一件值得注意的事情是日志记录下的进程也就是 AMSI 事件发生的进程的 ID 是7184,它是一个 svchost.exe 进程。 使用 Win32_Service WMI 类获取相应的服务将实际的进程解析为 WinRM 服务,考虑到 CIM cmdlet 使用的 WinRM 服务所使用的协议是WSMan,这就变得有意义了。
所以这真的很酷,作为防御者和终端安全供应商,我们得到了这种背景下可疑的 WMI 操作! 现在,WMI 服务通常非常嘈杂有很多噪音,操作系统定期使用 WMI 进行合法操作。 这难道不会淹没 AMSI 缓冲区的使用者吗? 经过试验,我注意到我显式启动的许多 WMI 操作没有被记录到日志中。 通过进一步的逆向,这背后的原因变得显而易见—- JAMSI::JAMSIRunScanner 只能在 JAMSI::JAMSIIsScanner 返回为 TRUE 时有条件地执行:
在简单地深入研究 JAMSI: : JAMSIIsScannerNeeded 的实现之后,很明显 WMI 操作上下文字符串有一个为它们计算的 CRC 校验和,并且只有当它们的校验和与值的白名单匹配时才会被记录下来:
在撰写本文时,白名单中存在以下 CRC 值: 0x788C9917,0x96B23E8A,0x8da804e,0xC0B29B3D,0xD16F4088,0xd61d2e7,0xEF726924,0x46B9D093,0xF837EFC3。
我从来没有花时间暴力破解恢复这些校验和的值,但考虑到 Win32_Process 创建操作已经被记录,这些校验和很可能是一个安全的假设,即这些校验和指的是常见的攻击者所使用的 WMI 攻击技术。 计算这些字符串的校验和很可能是一个隐晦式度量,这在 vbscript.dll 和 jscript.dll 中也会执行。
因此,这一部分的总结是——这是冷静和令人欣慰的发现,微软将继续为供应商和防御人员提供安全接口,否则他们将什么也看不到! 我敢肯定,一旦恢复了白名单,就会有很多绕过这个日志记录的方法,在安全性方面从来就没有什么银弹,这是朝着正确方向迈出的一大步!
你可能还有一个未回答的问题,就是如何解释从 Get-WinEvent 返回的 Properties 值中的事件字段。 下一节将介绍用于回答这个问题的方法。
AMSI 事件字段名恢复
将事件字段名与.ETL 跟踪关联的最简单的办法是将它们加载到事件查看器中,事件查看器将把跟踪转换为 EVTX 文件,并在引擎盖下解析出 AMSI ETW 提供程序事件清单,然后将字段名应用于已生成的相应的.evtx文件,你可以在详细信息视图中看到:
此外,你可以使用 perfview.exe 将 ETW 提供者模式恢复为 XML。 在恢复的架构中定义字段名的顺序与 Get-WinEvent 返回的 Properties 中的值的顺序相对应。
基于对 AMSIScanBuffer 函数的有限逆向研究,下面是事件 ID 1101(AMSI 扫描事件)中字段的描述:
· session:会话标识符。 如果通过调用 AmsiOpenSession 建立了 AMSI 扫描会话,则将填充此内容。 这个值是通过调用 IAMSIStream::GetAttribute 方法获得的,并指定 AMSI_ATTRIBUTE_SESSION 值。 如果填充了此值,则可以使用此值关联来自同一会话的多个扫描。 例如,如果执行一个混淆过的 PowerShell 脚本,那么理论上与这个脚本的执行相关的所有脚本块将具有相同的会话标识。· scanStatus: 这似乎是一个布尔值,我只见过它被设置为1。我没有再做进一步的调查研究。
· scanResult: 这是端点产品完成对缓冲区的扫描后返回的 AMSI_RESULT 值。 在本例中,1表示 AMSI_RESULT_NOT_DETECTED。· appname:通过 AmsiInitialize 的 appName 参数传递应用程序名称。 对于 fastprox.dll,appName 被硬编码为“ WMI”。· contentname: 如果被扫描的内容来自某个文件,则此字段包含完整的文件路径。
· contentsize: 据推测,如果内容是过滤的,这将反映过滤内容的大小,该大小值小于原始大小。 实际上,我还没有看到过滤内容。· originalsize: 未过滤内容的大小。 通过调用 IAMSIStream::GetAttribute 方法并指定 AMSI_ATTRIBUTE_CONTENT_SIZE 的值来获得该值。
· content: 被扫描的有效载荷的原始字节。 根据组件实现的不同,该字段的值是 Unicode 字符串或原始字节。 例如,在 clr.dll 的情况下,内存中的程序集加载的内容由被扫描的整个 PE 组成!
· hash: 被扫描的缓冲区的 SHA256 哈希值。
· contentFiltered: 指示内容是否被截断或过滤。 在撰写本文时,AMSIScanBuffer 将该字段设置为了 false。 目前没有逻辑将该值设置为 true。
总结
这篇文章的目的是建立一种识别 AMSI 组件的方法,研究它们的实现(使用 WMI 作为一个用例) ,并从 AMSI ETW 事件推断上下文。 这是攻击者寻求规避 AMSI 所需要的方法论。 这对于能够从消费 ETW 事件中获益的防御者也很有用。 虽然目前还不清楚微软是否会对所有组件的 AMSI 发布正式文档,但这种方法揭示了微软在持续投入 AMSI 的证据!
最后,我肯定会将 AMSI ETW 跟踪添加到我的恶意软件分析列表中,因为它提供了一个从严重混淆过的恶意软件样本中捕获脚本、 WMI 和 .Net 程序集内容的绝佳机会。
本文翻译自:https://posts.specterops.io/antimalware-scan-interface-detection-optics-analysis-methodology-858c37c38383如若转载,请注明原文地址: https://www.4hou.com/system/20714.html