Featured image of post 红队利用Windows事件日志隐藏恶意载荷的技术解析

红队利用Windows事件日志隐藏恶意载荷的技术解析

本文详细解析了如何利用Windows事件日志存储和检索二进制载荷的技术,包括事件日志结构、权限限制、载荷注入方法,以及通过C#程序实现载荷提取与执行的完整攻击链演示。

Windows事件日志用于红队行动

你是否知道Windows事件日志中可能隐藏着什么?
2022年5月,我收到一篇Threat Post文章,介绍了一种在野外发现的利用Windows事件日志维持持久性的新技术。我立即开始浏览这篇文章(原文链接:https://threatpost.com/attackers-use-event-logs-to-hide-fileless-malware/179484/ 及卡巴斯基原始报告:https://securelist.com/a-new-secret-stash-for-fileless-malware/106393/)。我既惊讶又沮丧地发现,利用Windows事件日志存储攻击载荷并进而维持持久性竟然如此简单。

文章发表时,我正在圣地亚哥参加WWHF: Way West 2022会议,因此计划回家后深入研究这一主题。
几周后,我终于有时间重新审视这篇文章,真正深入探究其工作原理、运作方式以及使用Windows事件日志作为载荷存储装置的限制(如果有的话)。

对我来说,最合理的起点是了解事件日志在Windows中的一些重要细节——基本上是基础知识。我不会用大量琐碎的细节让你感到无聊,但理解一些基础知识很重要,尤其是关于事件日志的创建以及它如何影响将日志用于攻击目的。

Windows事件日志基础

Windows事件日志包含来自操作系统、服务以及应用程序(如Office和SQL Server)的日志。日志使用结构化数据格式,使其易于搜索和分析。
访问Windows事件日志的最简单方法是使用事件查看器(evetvwr.exe)。

Windows系统的主要日志位于“Windows日志”中,该文件夹包含所有Windows系统标准的五个类别:

  • 应用程序
  • 安全
  • 安装程序
  • 系统
  • 转发的事件

事件查看器中还有一个名为“应用程序和服务日志”的文件夹,包含单个应用程序和基于硬件的事件的日志。Windows PowerShell日志将在此集合中找到。

每个日志条目都格式化为具有特定字段的通用结构。以下字段是日志分析中最常过滤的字段:

  • 日志/键 <– 例如应用程序
  • 源 <– 例如Outlook
  • 日期/时间
  • 事件ID
  • 任务类别 <– 应用程序定义
  • 级别
  • 计算机
  • 事件数据 <– 消息和二进制数据

Windows事件日志 – 用户约束

你是本地管理员吗?还是只是普通用户?
某些事件日志只有在你是本地管理员时才能写入,其他日志则任何人都可写入。虽然对于本篇博客文章来说不是超级重要,但根据本文后面技术的使用,这些用户无法写入某些日志的限制可能会发挥作用。

下面是一个漂亮的图表,显示了用户在常见Windows安装中各种事件日志的权限:

如果还不明显,让我明确记录:要能够在事件日志条目中存储载荷,你必须首先能够写入事件日志。根据你的用户上下文,你可能无法写入某些日志,例如系统日志,除非你在本地管理员的上下文中操作。

Windows事件日志 – 大小约束

另一个需要注意的限制是事件日志中可以存储的数据量有大小限制,基于事件消息字符串的最大字符限制31,839个字符。

现在所有基础知识都涵盖了——哦,等等,还有一件事……
不仅可以创建任意事件日志条目,如果你是本地管理员,还可以创建全新的事件日志。记住这一点,我们稍后会回来讨论。

现在基础知识已完成,我们可以跳入并开始创建一些事件日志条目。

使用PowerShell和Write-EventLog命令,可以简单地使用以下命令创建任意事件条目:

1
Write-Event -LogName $1 -Source $2 -EventID $3 -EventType Information -Category 0 -Message $4

但需要注意几点。首先,-LogName参数必须是你的用户上下文可以写入的有效日志;其次,-Source参数需要是在Windows注册表中注册为特定日志的源。

在注册表中,你可以在Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\找到事件日志,并在每个键中找到已注册到该事件日志的源列表。如上所示,我们选择使用事件日志“应用程序”和源“Edge”,因为Edge是注册到事件日志的有效源。

为了演示目的,我们选择任意事件ID 31337,但你可以使用任何你选择的事件ID。但是,如稍后所示,选择有效的事件ID有助于限制日志中出现异常的指标。

创建事件日志条目时,你需要使用-EntryType参数定义EntryType。有五种类型可以使用,但如果你试图不被发现,信息类型可能是最佳选择。

其次是类别,这是一个应用程序定义的字段,用于帮助过滤日志。这里我们将其设置为0,在事件查看器中查看时等同于无。

在事件查看器中,我们可以看到我们的事件日志条目已成功创建在应用程序日志中,事件ID为31337,消息为“Here be dragons”。

需要注意的一点是,如果你使用先前的命令为自己创建日志条目,你会在我们用户提供的消息“Here be dragons”之前看到以下文本:

“找不到源edge的事件ID 31337的描述。要么引发此事件的组件未安装在本地计算机上,要么安装已损坏。你可以在本地计算机上安装或修复该组件。如果事件源自另一台计算机,则显示信息必须与事件一起保存。”

此消息附加到我们用户提供的消息的原因是,在源Edge的注册表键中,有一个名为EventMessageFile的属性,指向包含与该源关联的事件消息的DLL文件。在我们的案例中,我们提供了一个在EventMessageFile中未找到的事件ID,因此导致我们在事件查看器中看到的日志条目消息。

如果你试图保持低调和未被检测到,建议你仅使用相关的源和后续事件ID。事件源在正常的日常事件日志条目中生成此类消息并不常见,因此如果分析师观察到该事件,消息可能会引起怀疑。

既然已经展示了创建事件日志条目是多么简单,下一步是弄清楚如何以及在哪里注入载荷。

如果你还记得Windows事件日志基础,条目的EventData字段支持消息和二进制数据。通过简单地向我们的PowerShell命令添加一个参数,我们可以使用-RawData参数在事件日志条目中包含二进制数据。

为了能够在日志条目中嵌入二进制数据,我们必须将其作为字节数组传递给Write-EventLog命令。有许多方法可以做到这一点,但我选择将包含我的数据的十六进制文字字符串转换为字节数组,然后将该变量传递给-RawData参数。

打开新日志条目并单击“详细信息”选项卡,我们发现我们包含的二进制数据以字节形式以及数据的ASCII版本很好地存储供我们查看。

砰!我们现在在日志条目中存储了用户定义的二进制数据。我想你可以看到这走向何方……

逻辑上的下一步是在日志条目中包含实际载荷,而不仅仅是一些文本。首先,我使用msfvenom生成了一个简单的Windows exec载荷,输出格式为十六进制文字字符串。

接下来,我们必须使用上面的载荷字符串创建一个新的事件日志条目。为了复制使用此技术的威胁行为者的行动,我们没有使用应用程序日志和源Edge,而是使用了密钥管理服务日志和源KmsRequests,如下面来自卡巴斯基SecureList文章的图像所示。

回顾事件查看器,我们可以看到我们的日志条目已创建,我们的二进制载荷安全地存储在里面。

这太棒了,但我们有一个问题;我们有一个存储的载荷,但还没有办法使用它。载荷检索时间。

可能有大约14,598,231种方法从我们创建的事件日志条目中提取载荷,但考虑到我们还需要执行载荷,我选择使用一个简单的C#程序,该程序将搜索密钥管理服务日志中事件ID为31337的长条目,然后从所述条目中提取二进制数据。

以下代码是一个非常基本且粗糙编写的概念验证,从事件日志密钥管理服务中的第一个条目提取二进制载荷数据,然后使用非常常见的shellcode注入技术将该二进制载荷注入到当前运行进程中。

此概念验证的代码可以在GitHub上找到:EventLogForRedTeams

使用Visual Studio编译PoC代码后,我们可以执行程序,从事件日志中的存储二进制载荷弹出calc.exe。

我们中的精明人会注意到代码确实抛出了一个看似致命的错误,但这并没有阻止calc.exe的成功执行。请记住,此代码设计为仅作为PoC勉强运行,因此事实上只有一个致命错误在我的书中是胜利。

好了,就是这样,二进制载荷存储在Windows事件日志中……等等,什么?你想要更多?我想也是,所以系好安全带,我们开始吧。

弹出calc.exe一切都很好,但利用这种技术我们可以做得更多。远程shell怎么样?你说Metasploit?在2022年,肯定不是。让我们试试看。

首先,我们需要使用msfvenom生成一个新的载荷。我选择使用载荷windows/x64/shell_reverse_tcp,主要是因为我觉得任何Metasploit载荷在2022年都是碰运气,但我发现无阶段载荷有更高的成功机会(但你的情况可能不同)。

创建新载荷的哈希文字字符串后,我必须使用新载荷创建一个新的事件日志条目。

由于C#代码采用简单的方法在事件日志中查找载荷,它只会从日志中找到的第一个条目中提取二进制数据,因此我在继续之前清空了密钥管理服务日志。

清空日志条目后,我可以创建带有更新二进制载荷的新日志条目。如下所示,我们的载荷再次安全地存储在事件日志条目中,等待某些东西使用它。

最后一步是使用参数-nvlp 1337设置nc监听器。设置好监听器后,是时候执行我们的程序来注入我们的shellcode并希望获得远程连接了。为了方便这一点,我从Windows虚拟机SSH到Kali Linux虚拟机以运行监听器。

如上图所示,在Windows 11 Pro主机上成功建立了一个会话,Windows Defender运行且没有关闭任何设置。

胜利!!!

嗯,几乎……

你看,当我开始这项研究工作

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计