前言
2023年10月份,Maxime Meignan在riskinsight发表了一篇名为A universal EDR bypass built in Windows 10[1] 的文章,其大致内容是作者发现了通过修改进程环境块(PEB)中的某几个字段,可以禁止进程产生特定的事件。
当时看到这篇文章后感到非常兴奋,原因是我在2023年8月份的文章 ETW生产者和消费者模型3 中记录了同样的发现,相比作者可惜的是没有继续深挖如何对字段进行修改。秉持着收藏就等于会了的原则,随时我就把文章纳入收藏夹以待后续研究,结果一放就是半年 : (
现在想起来还得多亏了Legacyy在2024年4月末写的一篇名为ETW-ByeBye: Disabling ETW-TI Without PPL[2] 的文章 ( 差点吃灰 :)。
闲话少叙,这篇文章将包含如下几点内容:
-
• 概括如何禁止进程产生特定事件
-
• 如何使用 SealighterTI[3] 订阅Threat-Intelligence ETW Provider
-
• 阐述Leagacyy文章中的问题
-
• 编写代码思路
文末日志文件可在 https://github.com/jseclab/wechat_public/tree/main/etw/p4 查看
正文
概括
此技术的核心是在PEB中有如下几个字段可以启用/禁用事件写入,我们通过NtSetInformationProcess可设置此值。
EnableReadVmLogging:虚拟内存读事件
EnableWriteVmLogging:虚拟内存写事件
EnableProcessSuspendResumeLogging:进程挂起/恢复事件
EnableThreadSuspendResumeLogging:线程挂起/恢复事件
按照Maxime Meignan所述,这并不适应于所有系统,在win10的特定区间系统可用。
SealighterTI
Threat-Intelligence ETW Provider通过事件提供了非常重要的威胁情报,包括上述提到的内存操作等,当然并不是什么“档次”的程序都可以随随便便订阅Threat-Intelligence ETW Provider的,通常只有PPL级别及其以上的才可以订阅,而SealighterTI结合了PPL的漏洞可以成功订阅Threat-Intelligence ETW Provider,这为对ETW感兴趣的研究人员提供了帮助。
PPL漏洞在Windows 10 v21H2 Build 19044.1826之后进行了修补,理论上SealighterTI在此之后就不能够使用了,作者也提到了另外的方法,可自行查看Note。
使用步骤:
-
• 在Release页面下载SealighterTI和sealighter_provider.man两个文件放在同一 文件夹内,虽然作者提供了debug版本,但是不建议下载。
-
• 打开sealighter_provider.man文件,替换所有的 !!SEALIGHTER_LOCATION!! 字段为SealighterTI全路径,比如 C:Program FilesSealighterTI.exe
-
• 以管理员运行cmd/powershell,执行 wevtutil im [sealigher_provider.man 全路径],比如 wevtutil im C:Program Filessealigher_provider.man
-
• 执行SealighterTI.exe -d
在停止记录之后,打开事件查看器,应当在应用程序和服务日志下生成Sealighter->Operational事件日志
当然我们需要有一个简单的测试程序帮助产生内存的读写事件,比如我采用的是每隔三秒钟对记事本进行内存写入操作
#include <windows.h>
#include <stdio.h>
int main()
{
BYTE buffer[0x100] = { 0 };
HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, 4548);
if (hProcess == NULL)
{
printf("error got handle , error code = %dn", GetLastError());
Sleep(3 * 1000);
return -1;
}
do {
DWORD oldProtect = 0;
if (VirtualProtectEx(hProcess, (LPVOID)0x7ff712c30000, 0x100, PAGE_READWRITE, &oldProtect))
{
if (WriteProcessMemory(hProcess, (LPVOID)0x7ff712c30000, buffer, 0x100, NULL)) printf("write memory successn");
else printf("error code 2 = %dn", GetLastError());
}
else
{
printf("error code 1 = %dn", GetLastError());
}
Sleep(3 * 1000);
} while (true);
return 1;
}
Leagacyy文章中的问题
上述我们已经学会如何使用SealighterTI记录事件,可以帮助我们验证此技术的正确性,接下来我们看Leagacyy的文章,内容我们就不说了,直接看他的代码部分。
-
• 首先既然是禁用,上述中的几个字段理应设置为false,而非true
ProcessLoggingInformation.EnableReadVmLogging = 1;
ProcessLoggingInformation.EnableWriteVmLogging = 1;
ProcessLoggingInformation.EnableProcessSuspendResumeLogging = 1;
ProcessLoggingInformation.EnableThreadSuspendResumeLogging = 1; -
• 使用NtSetInformationProcess时需指定设置的进程信息类型(为枚举值)以及对应的进程信息(通过特定的结构体来表示),比如这里的类型为ProcessEnableLogging,对应的承载进程信息结构体就是PROCESS_LOGGING_INFORMATION,Leagacyy 在文章中使用的PROCESS_LOGGING_INFORMATION结构体如下
typedef struct _PROCESS_LOGGING_INFORMATION
{
ULONG Flags;
ULONG EnableReadVmLogging;
ULONG EnableWriteVmLogging;
ULONG EnableProcessSuspendResumeLogging;
ULONG EnableThreadSuspendResumeLogging;
//ULONG EnableLocalExecProtectVmLogging; // New in Win11
//ULONG EnableRemoteExecProtectVmLogging; // New in Win11
ULONG Reserved = 26;
} PROCESS_LOGGING_INFORMATION, * PPROCESS_LOGGING_INFORMATION;
而我在systeminformer中找到的PROCESS_LOGGING_INFOMATION却是个联合体,且大小只有四个字节
typedef union _PROCESS_LOGGING_INFORMATION
{
ULONG Flags;
struct
{
ULONG EnableReadVmLogging : 1;
ULONG EnableWriteVmLogging : 1;
ULONG EnableProcessSuspendResumeLogging : 1;
ULONG EnableThreadSuspendResumeLogging : 1;
ULONG EnableLocalExecProtectVmLogging : 1;
ULONG EnableRemoteExecProtectVmLogging : 1;
ULONG Reserved : 26;
};
} PROCESS_LOGGING_INFORMATION, *PPROCESS_LOGGING_INFORMATION;
根据Maxime Meignan文章中NtSetInformationProcess伪代码可以看到当传入ProcessEnableLogging时会检查ProcessInformationLength的大小是否小于4
-
• 第三个问题也是最为重要的问题,在调用NtSetInformationProcess时,第一个参数应当传入谁的句柄?
回答这个问题,我们应当使用windbg在PsIsProcessLoggingEnabled处下断点,并检查第一个参数也就是eprocess,查看其中的ImageFileName,事实证明当我使用测试程序不停的写入记事本程序时,PsIsProcessLoggingEnabled检查的实际上时记事本程序中字段开启有无,而非测试程序的,这一点和Maxime Meignan文章中的PsIsProcessLoggingEnabled伪代码相呼应。
代码思路
-
• 提权,NtSetInformationProcess会检查调用进程是否有 SeDebugPrivilege or SeTcbPrivilege权限。
-
• 以PROCESS_SET_LIMITED_INFORMATION权限打开目标进程并获得句柄
-
• 动态获取NtSetInformationProcess函数地址
-
• 构造PROCESS_LOGGING_INFORMATION参数并调用NtSetInformationProcess
-
• Enjoy
写在最后:通过上述步骤我成功复现了整个过程,我使用SealighterTI分别获得了禁用记事本内存操作的前后的所有事件,并导出到记事本上传至 github[4] ,有兴趣的可以查看。
testRW.txt为禁用前产生的事件,testRW-bye为禁用后产生的事件,3900为写入程序pid,4548为记事本程序pid,禁用后的内存读写事件未被记录。
引用
[1]:https://www.riskinsight-wavestone.com/en/2023/10/a-universal-edr-bypass-built-in-windows-10/
[2]:https://www.legacyy.xyz/defenseevasion/windows/2024/04/24/disabling-etw-ti-without-ppl.html#building-a-poc
[3]:https://github.com/pathtofile/SealighterTI?tab=readme-ov-file
[4]:https://github.com/jseclab/wechat_public/tree/main/etw/p4
原文始发于微信公众号(无名之):关于ETW的这点,他错了
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论