0x00 前言

Windows XML Event Log (EVTX)单条日志清除系列文章的第四篇,介绍第二种删除当前系统单条日志记录的方法:获得日志服务Eventlog对应进程中指定日志文件的句柄,通过Dll注入获得该句柄的操作权限,利用该句柄实现日志文件的修改。

其他几篇:

Windows XML Event Log (EVTX)单条日志清除(三)——通过解除文件占用删除当前系统单条日志记录

Windows XML Event Log (EVTX)单条日志清除(二)——程序实现删除evtx文件的单条日志记录

Windows XML Event Log (EVTX)单条日志清除(一)——删除思路与实例


0x01 简介

本文将要介绍以下内容:

0x02 利用思路

系统启动日志服务Eventlog后,会以独占模式打开日志文件,导致其他进程无法打开该日志文件,也就无法进行修改操作

那么,如果我们通过Dll注入进入进程的内存,接着获得指定日志文件的句柄,能否获得该日志文件的操作权限呢?

0x03 枚举日志服务Eventlog对应进程的所有句柄,获得指定日志文件的句柄

1、利用工具processhacker获得指定日志文件的句柄

下载地址:

https://processhacker.sourceforge.io/

(1)获得日志服务Eventlog对应进程的pid

执行如下powershell代码:

Get-WmiObject -Class win32_service -Filter "name = 'eventlog'" | select -exp ProcessId

(2)运行processhacker

根据pid找到进程,查看Properties->Handles

能够获得当前进程的所有句柄信息

如下图

image.png

可以看到C:\Windows\System32\winevt\Logs\Security.evtx对应的Handle值为0x1c8

2、通过c++程序实现获得指定日志文件的句柄

查看processhacker的源码,寻找实现方法

代码位置:

https://github.com/processhacker/processhacker/blob/e2d793289dede80f6e3bda26d6478dc58b20b7f8/ProcessHacker/hndlprv.c#L307

获得参考资料:

  • On Windows 8 and later, NtQueryInformationProcess with ProcessHandleInformation is the most efficient method.

  • On Windows XP and later, NtQuerySystemInformation with SystemExtendedHandleInformation.

  • Otherwise, NtQuerySystemInformation with SystemHandleInformation can be used.

于是,挑选第三个方法尝试实现

注:

经测试,第三个方法适用于Win7和更新版本的操作系统

利用NtQuerySystemInformation查询SystemHandleInformation能够获得所有进程的句柄信息

挑选出日志进程中的所有句柄

接着通过NtDuplicateObject获取句柄的名称和具体的数值信息

最后筛选出想要查找的句柄,输出Handle值

完整实现代码已开源,下载地址如下:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/GetPIDandHandle(evtx).cpp

代码实现了根据输入的关键词进行搜索,获得对应的句柄名称和Handle值

测试如下图

image.png

成功获得日志服务Eventlog对应进程的pid,security.evtx对应的Handle值为0x1c8

0x04 通过Dll注入获得该句柄的操作权限

通过NtCreateThreadEx + LdrLoadDll实现Dll注入的代码可参考:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/NtCreateThreadEx%20%2B%20LdrLoadDll.cpp

注:

注入成功后需要FreeDll

注入成功后,使用获得到的Handle值作为函数CreateFileMapping()的第一个参数,创建一个文件映射内核对象

然后调用函数MapViewOfFile()将文件数据映射到进程的地址空间

接下来修改内存中的数据,删除单条日志记录

最后调用函数FlushViewOfFile(),将内存数据写入磁盘

0x05 进程间消息传递的方法

在实际使用过程中,整个实现日志记录删除功能的代码要放在Dll中,而通过CreateRemoteThread无法向Dll传入参数,这就导致无法删除指定EventlogRecordId的日志

这里可以借助进程间的消息传递

实现方法有多种,例如信号、管道、消息队列和共享内存,甚至是读写文件

由于在0x04部分使用了函数CreateFileMapping()创建一个文件映射内核对象,所以进程间消息传递也使用内存映射的方式

创建一个共享内存,代码可参考:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/OpenFileMapping.cpp

代码中创建了两个内存映射对象,并且指定了函数CreateFileMapping()的访问权限为允许任何人访问该对象,即函数CreateFileMapping()的第二个参数

通常情况下,该值设置为NULL,表示默认访问权限,但在注入的Dll中必须设置为允许任何人访问该对象,否则提示拒绝访问

原因如下:

Dll注入svchost.exe后,权限为System,默认访问权限无法访问由用户创建的内存映射文件对象,必须指定为允许任何人访问该对象

当然,如果是两个用户权限的进程进行消息传递,函数CreateFileMapping()的第二个参数为NULL即可

读取指定共享内存,代码可参考:

https://github.com/3gstudent/Homework-of-C-Language/blob/master/OpenFileMapping.cpp

代码中读取这两个内存映射对象,添加了数据类型转换的功能(字符串转int)

0x06 程序实现流程

1、自己解析格式,实现日志删除

删除的关键代码如下:

https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/DeleteRecordofFile.cpp

代码实现了删除文件c:\test\Setup.evtx中的一条日志(EventRecordID=14),新文件保存为c:\test\SetupNew.evtx

整个实现流程分成两部分:

1.启动程序(Loader-rewriting.cpp)

代码地址:

https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/Loader-rewriting.cpp

流程如下:

  1. 获得日志服务Eventlog对应进程的pid

  2. 枚举日志服务Eventlog对应进程的的所有句柄,获得指定日志文件的句柄

  3. 创建两个内存映射,用于向dll传递日志文件的句柄和需要删除日志的EventRecordID

  4. 向日志服务Eventlog对应的进程注入Dll

  5. 释放Dll

  6. 关闭内存映射

2.注入的Dll(Dll-rewriting.cpp)

代码地址:

https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/Dll-rewriting.cpp

流程如下:

  1. 分别从两个内存映射读取消息,将读取到的内容从字符串转换成int类型,获得日志文件的句柄及需要删除日志的EventRecordID

  2. 调用函数CreateFileMapping(),传入日志文件的句柄,创建一个文件映射内核对象

  3. 调用函数MapViewOfFile()将文件数据映射到进程的地址空间

  4. 修改内存数据,删除指定日志

  5. 调用函数FlushViewOfFile(),将内存数据写入磁盘

  6. 关闭日志文件的内存映射

测试如下图

image.png

2、使用WinAPI EvtExportLog,过滤出想要删除的内容

可供参考的代码:

https://github.com/360-A-Team/EventCleaner/blob/master/EventCleaner/EventCleaner.cpp#L528

我按照这个思路写的代码:

https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/DeleteRecordofFileEx.cpp

代码实现了调用Windows API EvtExportLog对日志文件进行筛选,去除指定日志后,将剩下的日志内容保存为新文件temp.evtx

整个实现流程分成三部分:

1.日志删除程序(DeleteRecord-EvtExportLog.cpp)

代码地址:

https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/DeleteRecord-EvtExportLog.cpp

流程如下:

  1. 指定日志文件和需要删除日志的EventRecordID

  2. 生成新的日志文件temp.evtx

2.启动程序(Loader-EvtExportLog.cpp)

代码地址:

https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/Loader-EvtExportLog.cpp

流程如下:

  1. 获得日志服务Eventlog对应进程的pid

  2. 枚举日志服务Eventlog对应进程的的所有句柄,获得指定日志文件的句柄

  3. 创建三个内存映射,用于向dll传递日志文件的句柄、新日志文件的长度和新日志文件的内容

  4. 向日志服务Eventlog对应的进程注入Dll

  5. 释放Dll

  6. 关闭内存映射

3.注入的Dll(Dll-EvtExportLog.cpp)

代码地址:

https://github.com/3gstudent/Eventlogedit-evtx--Evolution/blob/master/Dll-EvtExportLog.cpp

流程如下:

  1. 从第一个内存映射获得日志文件的句柄,将读取到的内容从字符串转换成int类型

  2. 从第二个内存映射获得新日志文件的长度

  3. 根据新日志文件的长度调整内存映射的读取长度,从第三个内存映射获得新日志文件的内容

  4. 调用函数CreateFileMapping(),传入日志文件的句柄,创建一个文件映射内核对象

  5. 调用函数MapViewOfFile()将文件数据映射到进程的地址空间

  6. 修改内存数据,覆盖为新日志文件的内容

  7. 调用函数FlushViewOfFile(),将内存数据写入磁盘

  8. 关闭日志文件的内存映射

测试如下图

image.png

注:

对于以上两种方法,删除setup.evtx是没有问题的,删除system.evtxsecurity.evtx会存在因为竞争条件导致删除失败的情况

0x07 小结

本文介绍了第二种删除当前系统单条日志记录的方法:获得日志服务Eventlog对应进程中指定日志文件的句柄,通过Dll注入获得权限,利用该句柄实现日志文件的修改 某些情况下,dll注入会失败,那么是否还有删除当前系统单条日志记录的方法呢?安全脉搏下一篇文章将会介绍。



源链接

Hacking more

...