根据 Microsoft、Google、Kaspersky、Checkpoint 和其他行业参与者的披露,野外 Windows 本地提权 (LPE) 零日漏洞越来越普遍,并且是复杂的网络犯罪和 APT 攻击武器库中的重要组成部分。对于检测工程师来说,仔细检查这些公开可用的样本并评估可能的检测途径非常重要。
本文不会深入探讨漏洞的根本原因或具体细节;但是,我们会提供指向相关漏洞研究文章的链接。我们将基于使用 Elastic Defend 功能的动态行为分析来评估检测方法。
案例 1 - 通用日志文件系统
通用日志文件系统 (CLFS) 是一种通用的日志记录服务,可供需要高性能事件日志记录的软件客户端使用。 Microsoft 安全更新指南显示,自 2018 年以来,已修补了 30 多个 CLFS 漏洞,其中 5 个是在 2023 年的勒索软件攻击中观察到的。2024 年也以针对同一 CLFS 驱动程序的漏洞报告(由多位研究人员提交)开始。
您可以在此处找到一系列深入研究 CLFS 漏洞内部原理的优秀文章。这些漏洞的一个共同点是,它们利用一些 clfsw32.dll
API(CreateLogFile
和 AddLogContainer
)来创建和操作 BLF 日志,使它们能够写入或破坏内核模式地址。与其他利用原语结合使用,这可能会导致成功提升权限。
基于这些漏洞的细节,可以设计一个高级别检测来识别不寻常的进程。例如,以低或中等完整性运行的进程可以创建 BLF 文件,然后出乎意料地执行系统完整性级别的活动(生成系统子进程,使用系统权限进行 API 调用、文件或注册表操作)。
以下 EQL 查询可用于关联 Elastic Defend 文件事件,其中调用堆栈包含用户模式 API CreateLogFile
或 AddLogContainerSet
的引用,特别是当以普通用户身份运行时,随后创建以 SYSTEM 身份运行的子进程时
sequence with maxspan=5m
[file where event.action != "deletion" and not user.id : "S-1-5-18" and user.id != null and
_arraysearch(process.thread.Ext.call_stack, $entry,
$entry.symbol_info: ("*clfsw32.dll!CreateLogFile*", "*clfsw32.dll!AddLogContainerSet*"))] by process.entity_id
[process where event.action == "start" and user.id : "S-1-5-18"] by process.parent.entity_id
以下是 CVE-2022-24521 上的匹配示例,其中 cmd.exe
以 SYSTEM 身份启动
以下 EQL 查询使用与前一个查询类似的逻辑,但它不是生成子进程,而是查找在 BLF 文件事件之后具有 SYSTEM 权限的 API、文件或注册表活动
sequence by process.entity_id
[file where event.action != "deletion" and not user.id : "S-1-5-18" and user.id != null and
_arraysearch(process.thread.Ext.call_stack, $entry, $entry.symbol_info : ("*clfsw32.dll!CreateLogFile*", "*clfsw32.dll!AddLogContainerSet*"))]
[any where event.category : ("file", "registry", "api") and user.id : "S-1-5-18"]
until [process where event.action:"end"]
以下屏幕截图与 CLFS 漏洞提升权限后的清理阶段匹配(使用系统权限删除文件)
除了之前的两个行为检测之外,我们还可以利用 YARA 来搜索导入用户模式 API CreateLogFile
或 AddLogContainerSet
的未签名 PE 文件,以及来自 clfsw32.dll
的数量异常多的函数(正常的 CLFS 客户端程序会从同一 DLL 导入更多函数)
import "pe"
rule lpe_clfs_strings {
strings:
$s1 = "NtQuerySystemInformation"
$s2 = "clfs.sys" nocase
condition:
uint16(0)==0x5a4d and (pe.imports("clfsw32.dll", "CreateLogFile") or pe.imports("clfsw32.dll", "AddLogContainer")) and all of ($s*)
}
rule lpe_clfs_unsigned {
condition:
uint16(0)==0x5a4d and pe.number_of_signatures == 0 and filesize <= 200KB and
(pe.imports("clfsw32.dll", "CreateLogFile") or pe.imports("clfsw32.dll", "AddLogContainer")) and
not (pe.imports("clfsw32.dll", "ReadLogRecord") or pe.imports("clfsw32.dll", "CreateLogMarshallingArea"))
}
以下是使用 Elastic 的 YARA 规则进行 CVE-2023-2825 的 VT 匹配示例
CVE-2023-2825 的 YARA 规则匹配
案例 2 - Windows DWM 核心库 EoP
桌面窗口管理器 (dwm.exe
) 自 Windows Vista 以来一直是 Microsoft Windows 中的合成窗口管理器。此程序启用硬件加速来呈现 Windows 图形用户界面,并具有高权限;但是,低权限的用户可以与 DWM 进程交互,这会大大增加攻击面。
安全研究员 Quan Jin 报告了针对 CVE-2023-36033 的野外漏洞利用,Google Project Zero 随后发布了详细的 文章,解释了该漏洞利用的阶段。
根据我们的理解,DWM 核心库 (dwmcore.dll
) 漏洞利用很可能会在以窗口管理器\DWM 用户权限运行时在 dwm.exe
进程中触发 shellcode 执行。请注意,这是高完整性,但还不是 SYSTEM。
在 Elastic Defend 上引爆 ITW 公开样本确实会触发自注入 shellcode 警报。在没有事先了解和上下文的情况下,人们可能会将其与通用代码注入警报或误报混淆,因为它是由 Microsoft 可信系统二进制文件进行的自注入警报,具有正常的父进程且未加载恶意库。
以下 KQL 搜索可用于查找类似的 shellcode 警报
event.code : "shellcode_thread" and process.name : "dwm.exe" and user.name : DWM*
除了 shellcode 执行之外,我们还可以通过基准化子进程和文件活动来查找 dwm.exe
中的不寻常活动。在下面,我们可以看到 dwm.exe
由于漏洞利用而生成 cmd.exe
的示例
根据我们的遥测可见性,dwm.exe
很少生成合法的子进程。可以使用以下检测来查找异常子进程。
process where event.action == "start" and
process.parent.executable : "?:\\Windows\\system32\\dwm.exe" and user.id : ("S-1-5-90-0-*", "S-1-5-18") and process.executable : "?:\\*" and
not process.executable : ("?:\\Windows\\System32\\WerFault.exe", "?:\\Windows\\System32\\ISM.exe", "?:\\Windows\\system32\\dwm.exe")
为了将权限从窗口管理器\DWM 用户提升到 SYSTEM,shellcode 会将 DLL 丢弃到磁盘,并在 dwm.exe
进程内的 kernelbase!MapViewOfFile
调用上放置 JMP 钩子。然后,它通过执行 shutdown /l
命令来触发注销。
注销操作会触发以 SYSTEM 用户身份运行的 LogonUI.exe
进程的执行。LogonUI.exe
进程将与桌面窗口管理器进程进行通信,类似于任何桌面 GUI 进程,这将编组/解组 Direct Composition 对象。
dwm.exe
内的 MapViewOfFile
钩子会监视映射的堆内容。它会修改该内容,使用另一组精心制作的小工具,以便在 LogonUI.exe
进程中解组资源堆数据时,执行已丢弃 DLL 的 LoadLibraryA
调用。
此处有两个主要的检测点:当 dwm.exe
将 PE 文件丢弃到磁盘时,以及当 LogonUI.exe
加载 DLL 时,调用堆栈指向 dcomp.dll
,这是编组/解组 Direct Composition 对象的指示。
下面是一个 KQL 查询,用于在文件事件和恶意软件警报中查找 dwm.exe
将 PE 文件丢弃到磁盘的情况
(event.category :"file" or event.code :"malicious_file") and
process.name :"dwm.exe" and user.id:S-1-5-90-0-* and
(file.extension :(dll or exe) or file.Ext.header_bytes :4d5a*)
下面是一个检测 EQL 查询,用于查找 LogonUI DLL 加载劫持。
library where process.executable : "?:\\Windows\\System32\\LogonUI.exe" and
user.id : "S-1-5-18" and
not dll.code_signature.status : "trusted" and
process.thread.Ext.call_stack_summary : "*combase.dll|dcomp.dll*"
案例 3 - Windows 激活上下文 EoP
CVE-2022-41073 是另一个有趣的野外漏洞。核心漏洞是用户可以在模拟期间为特权进程重新映射根驱动器 (C:\
)。这个特定样本欺骗 printfilterpipelinesvc.exe
进程加载任意 DLL,方法是在客户端服务器运行时子系统 (CSRSS) 中的激活上下文生成期间,将 C:\
驱动器重定向到 C:\OneDriveRoot
。然后,它伪装成 C:\Windows\WinSxS
目录,非特权用户无法写入该目录。
从行为角度来看,它属于由低/中等完整性进程丢弃的 SYSTEM 完整性进程加载 DLL 的类别。还有伪装成合法 Windows WinSxS 文件夹的标记。
以下 EQL 搜索可用于查找伪装成可信系统文件夹进行重定向的类似尝试
any where (event.category in ("file", "library") or event.code : "malicious_file") and
(
file.path : ("C:\\*\\Windows\\WinSxS\\*.dll", "C:\\*\\Windows\\system32\\*.dll", "C:\\*\\Windows\\syswow64\\*.dll", "C:\\*\\Windows\\assembly\\NativeImages*.dll") or
dll.path : ("C:\\*\\Windows\\WinSxS\\*.dll", "C:\\*\\Windows\\system32\\*.dll", "C:\\*\\Windows\\syswow64\\*.dll", "C:\\*\\Windows\\assembly\\NativeImages*.dll")
)
这也匹配此通用端点检测,该检测查找由提升的系统本地进程加载的不受信任的模块
通用行为检测
上面提供的示例说明了每个漏洞都具有不同的特征。利用方法因原语的灵活性而异,例如写入地址、执行 shellcode、加载任意 DLL 或创建文件。某些系统组件可能比其他组件存在更多漏洞,因此需要专门的检测工作(例如,CLFS、win32k)。
尽管如此,这些漏洞的最终目标和影响仍然是一致的。这突出了制定更有效检测策略的机会。
特权提升可以以各种形式表现出来
- 一个低/中等完整性进程生成一个提升的子进程
- 一个低/中等完整性进程将代码注入到提升的进程中
- 一个系统完整性进程意外加载一个不受信任的 DLL
- 一个系统本地进程意外丢弃 PE 文件
- 一个低/中等完整性进程将文件丢弃到受系统保护的文件夹中
- 一个用户模式进程写入内核模式地址
利用 Elastic Defend 的功能,我们可以设计检测并查找上述每种可能性。
低/中等完整性进程生成一个提升的子进程:
sequence with maxspan=5m
[process where event.action == "start" and
process.Ext.token.integrity_level_name in ("medium", "low")] by process.entity_id
[process where event.action == "start" and
process.Ext.token.integrity_level_name == "system" and user.id : "S-1-5-18"] by process.parent.entity_id
在利用易受攻击驱动程序 (Zemana zam64.sys
) 生成作为 SYSTEM 的 cmd.exe
的示例中匹配的示例
低/中等完整性进程将代码注入到提升的进程中:
这是一个ES|QL 查询,用于查找罕见的跨进程 API 调用
from logs-endpoint.events.api*
| where process.Ext.token.integrity_level_name in ("medium", "low") and Target.process.Ext.token.integrity_level_name == "system" and
process.Ext.api.name in ("WriteProcessMemory", "VirtualProtect", "VirtualAllocEx", "VirtualProtectEx", "QueueUserAPC", "MapViewOfFile", "MapViewOfFileEx")
| stats occurrences = count(*), agents = count_distinct(host.id) by process.Ext.api.name, process.executable, Target.process.executable
| where agents == 1 and occurrences <= 100
当我们运行此查询时,会获得 LPE 漏洞利用程序,这些漏洞利用程序在通过令牌交换提升后注入到 winlogon.exe
中
系统完整性进程意外加载一个不受信任的 DLL
这是一个 ES|QL 查询,用于查找由提升的 Microsoft 二进制文件加载的罕见未签名 DLL
from logs-endpoint.events.library-*
| where host.os.family == "windows" and event.action == "load" and
starts_with(process.code_signature.subject_name, "Microsoft") and
user.id in ("S-1-5-18", "S-1-5-19", "S-1-5-20") and
process.code_signature.status == "trusted" and
dll.Ext.relative_file_creation_time <= 500 and
(dll.code_signature.exists == false or dll.code_signature.trusted == false) and
/* excluding noisy DLL paths */
not dll.path rlike """[C-F]:\\Windows\\(assembly|WinSxS|SoftwareDistribution|SystemTemp)\\.+\.dll""" and
/* excluding noisy processes and potentially unrelated to exploits - svchost must be covered by a dedicated hunt to exclude service dlls and COM */
not process.name in ("rundll32.exe", "regsvr32.exe", "powershell.exe", "msiexec.exe", "svchost.exe", "w3wp.exe", "mscorsvw.exe", "OfficeClickToRun.exe", "SetupHost.exe", "UpData.exe", "DismHost.exe")
| stats occurrences = count(*), host_count = count_distinct(host.id) by dll.name, process.name
/* loaded once and the couple dll.name process.name are present in one agent across the fleet */
| where occurrences == 1 and host_count == 1
一个系统本地进程意外丢弃 PE 文件
以下 ES|QL 查询可用于搜索以下实例:具有低可执行文件创建历史记录的特权 Microsoft 签名二进制文件,并且仅限于跨受监视主机群的一个代理
from logs-endpoint.events.file-*
| where @timestamp > now() - 30 day
| where host.os.family == "windows" and event.category == "file" and event.action == "creation" and user.id in ("S-1-5-18", "S-1-5-19", "S-1-5-20", "S-1-5-90-0-*") and
starts_with(file.Ext.header_bytes, "4d5a") and process.code_signature.status == "trusted" and
starts_with(process.code_signature.subject_name, "Microsoft") and
process.executable rlike """[c-fC-F]:\\Windows\\(System32|SysWOW64)\\[a-zA-Z0-9_]+.exe""" and
not process.name in ("drvinst.exe", "MpSigStub.exe", "cmd.exe")
| keep process.executable, host.id
| stats occurrences = count(*), agents = count_distinct(host.id) by process.executable
| where agents == 1 and occurrences == 1
用户模式进程写入内核模式地址
破坏PreviousMode 是一种广泛流行的利用技术。覆盖 KTHREAD 结构中的这一个字节会绕过系统调用中的内核模式检查,例如 NtReadVirtualMemory
或 NtWriteVirtualMemory
,允许用户模式攻击者读取和写入任意内核内存。
在 x64 上,虚拟地址空间分为用户模式地址,范围从 0x00000000 00000000
到 0x0000FFFF FFFFFFFF
,内核模式地址范围从 0xFFFF0000 00000000
到 0xFFFFFFFF FFFFFFFF
。以下 EQL 查询可用于检测目标地址是内核模式地址的 API NtReadVirtualMemory
或 NtReadVirtualMemory
调用,这是一种异常行为
api where process.pid != 4 and process.Ext.api.name : "WriteProcessMemory"
and process.executable != null and
/* kernel mode address range - decimal */
process.Ext.api.parameters.address > 281474976710655
以下是这些警报在利用此原语的漏洞利用中触发的示例
结论
检测特定漏洞的特权提升需要深入了解漏洞及其利用方法,而这并非普遍知识。因此,投资于通用行为检测机制,重点关注漏洞利用对系统的影响以及常用的原语,例如 KASLR 绕过、令牌交换、PreviousMode 滥用和其他方法,证明更加有效。但是,对于 CLFS 和 win32k 等高度针对性的 Windows 系统组件,专用检测始终有价值,理想情况下是行为和 YARA 的组合。
尽管技术复杂且缺乏常见原语的日志,但蓝队不应忽视漏洞利用和漏洞研究内容;相反,他们应该努力理解并应用它。此外,通过 VirusTotal 或类似的野外 LPE 漏洞利用样本与防御社区共享将进一步促进检测控制的测试和增强。
参考
- https://i.blackhat.com/USA-22/Thursday/us-22-Jin-The-Journey-Of-Hunting-ITW-Windows-LPE-0day-wp.pdf
- https://securelist.com/windows-clfs-exploits-ransomware/111560/
- https://www.zscaler.com/blogs/security-research/technical-analysis-windows-clfs-zero-day-vulnerability-cve-2022-37969-part2-exploit-analysis
- https://googleprojectzero.github.io/0days-in-the-wild/rca.html
- https://conference.hitb.org/hitbsecconf2023ams/session/hunting-windows-desktop-window-manager-bugs/
- https://research.checkpoint.com/2024/raspberry-robin-keeps-riding-the-wave-of-endless-1-days/