Universally Evading Sysmon and ETW

栏目: IT技术 · 发布时间: 4年前

内容简介:TheThere's been some great research into this by

The source code and latest release are both available.

Sysmon and windows event log are both extremely powerful tools in a defender's arsenal. Their very flexible configurations give them a great insight into the activity on endpoints, making the process of detecting attackers a lot easier. It's for this reason that I'm going to lead you through my journey in defeating them ;)

There's been some great research into this by xpn and matterpreter . Their solutions are both good but don't quite reach my needs of a fully universal bypass. Metterpreter's method of unloading the driver does technically, but unloading the driver feels like a dirty way of doing it. Especially due to the number of very telling events triggered from it.

In order to be able to figure out how to bypass it, it's vital to first understand how it works. This post by @dotslashroot gives a really good insight into this and I really recommend you read it before carrying on.

We now understand that ETW (Event Tracing for Windows) is responsible for handling the reporting of the events caught in the kernel drivers' callbacks, but how does sysmon's user mode process actually report it?

Firing up Ghidra and throwing in sysmon64.exe , we can see that it uses the ReportEventW Windows API call to report the event.

Universally Evading Sysmon and ETW

Now that we know this, it would be possible to just hook this call and filter/block events from there... but what's the point in that? We would still need admin privs to do that and I think we could put them to better use.

Going deeper down the call chain and looking at ReportEventW in ADVAPI32.dll we can see that its essentially a wrapper around EtwEventWriteTransfer which is defined in NTDLL.dll.

Universally Evading Sysmon and ETW

By examining EtwEventWriteTransfer we can see it calls the kernel function NtTraceEvent which is defined inside ntoskrnl.exe.

Universally Evading Sysmon and ETW

We now know that this function will be called by any user mode process that wants to report an event, Awesome! Here's a quick diagram to visualize this process.

Universally Evading Sysmon and ETW

Now that we know what kernel function it is that we want to target let's focus on testing to see if this will actually work. To do this I'm going to be using WinDBG kernel debugging, more information on that can be found here .

I'll start by setting a breakpoint at nt!NtTraceEvent then once this breakpoint is hit I will then patch the very start of the function with a ret . This will force the function to return straight away, before any of the event reporting code is run.

Universally Evading Sysmon and ETW

And it worked! If you look below you will see that I'm able to launch a powershell prompt without any sysmon events being triggered.

Universally Evading Sysmon and ETW

So now we have a working PoC its time to start writing code. The code we want to write will need to hook NtTraceEvent and give us the choice if we want to report the event or not. Since the function we are targeting is a kernel function we will need to have our hooking code running inside kernel space as well. There are two main problems we are going to encounter when we try to do this.

Luckily there are two super cool projects already around for the purpose of defeating them, KDU by @hFireF0x and InfinityHook . I won't go into detail about how they both work as there's a lot of information at their respective links about that, But I'm happy because this saved me a lot of time as I don't need to write my own bypasses.

I'll start by writing the code to run in the kernel, a link to it all can be found here . Right at the start of the DriverEntry we are going to need to locate the export of both NtTraceEvent and IoCreateDriver . The reason we need to find IoCreateDriver is because of KDU. It will load our driver by loading and exploiting a signed driver and then bootstrap ours into kernel space, this method of loading our driver will mean that both the DriverObject and RegistryPath passed to DriverEntry will be incorrect. But because we need to be able to communicate with our user mode process (so we know when to report and block events) we will need to create a valid DriverObject . We can do this by calling IoCreateDriver and giving it the address of our DriverInitialize routine, our DriverInitialize will then be called and passed a valid DriverObject which can then finally be used to create an IOCTL, letting us speak to user mode. This code snippet is below.

NTSTATUS DriverEntry(
	_In_ PDRIVER_OBJECT DriverObject,
	_In_ PUNICODE_STRING RegistryPath)
{
	NTSTATUS        status;
	UNICODE_STRING  drvName;

	UNREFERENCED_PARAMETER(DriverObject);
	UNREFERENCED_PARAMETER(RegistryPath);

	DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "[+] infinityhook: Loaded.\r\n");

	OriginalNtTraceEvent = (NtTraceEvent_t)MmGetSystemRoutineAddress(&StringNtTraceEvent);
	OriginalIoCreateDriver = (IoCreateDriver_t)MmGetSystemRoutineAddress(&StringIoCreateDriver);

	if (!OriginalIoCreateDriver)
	{
		DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "[-] infinityhook: Failed to locate export: %wZ.\n", StringIoCreateDriver);
		return STATUS_ENTRYPOINT_NOT_FOUND;
	}

	if (!OriginalNtTraceEvent)
	{
		DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "[-] infinityhook: Failed to locate export: %wZ.\n", StringNtTraceEvent);
		return STATUS_ENTRYPOINT_NOT_FOUND;
	}

	RtlInitUnicodeString(&drvName, L"\\Driver\\ghostinthelogs");
	status = OriginalIoCreateDriver(&drvName, &DriverInitialize);

	DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "[+] Called OriginalIoCreateDriver status: 0x%X\n", status);

	NTSTATUS Status = IfhInitialize(SyscallStub);
	if (!NT_SUCCESS(Status))
	{
		DbgPrintEx(DPFLTR_DEFAULT_ID, DPFLTR_INFO_LEVEL, "[-] infinityhook: Failed to initialize with status: 0x%lx.\n", Status);
	}

	return Status;
}

Once we have located our exports and got a valid DriverObject we can now use InfinityHook to initialize our NtTraceEvent hook. The function IfhInitialize does this. I call IfhInitialize and pass it a pointer to my callback. This callback will be hit every time a syscall is made. The callback is given a pointer to the address of the function about to be called. Having access to this pointer means we can change it to point to the address of our hooked function. The callback code is shown below.

void __fastcall SyscallStub(
	_In_ unsigned int SystemCallIndex,
	_Inout_ void** SystemCallFunction)
{
	UNREFERENCED_PARAMETER(SystemCallIndex);

	if (*SystemCallFunction == OriginalNtTraceEvent)
	{
		*SystemCallFunction = DetourNtTraceEvent;
	}
}

This code will redirect every call to NtTraceEvent to our DetourNtTraceEvent . The code for DetourNtTraceEvent is shown below.

NTSTATUS DetourNtTraceEvent(
	_In_ PHANDLE TraceHandle,
	_In_ ULONG Flags,
	_In_ ULONG FieldSize,
	_In_ PVOID Fields)
{

	if (HOOK_STATUS == 0)
	{
		return OriginalNtTraceEvent(TraceHandle, Flags, FieldSize, Fields);
	}

	return STATUS_SUCCESS;
}

This code is very simple. It'll check to see if HOOK_STATUS (set by the user mode process via an IOCTL) is 0, if it is then it will carry out a call to NtTraceEvent , therefore reporting the event. If HOOK_STATUS is nonzero it will just return STATUS_SUCCESS signifying that the event was reported successfully, which of course it wasn't. It would be cool if someone could figure out how to parse the Fields parameter so it would be possible to apply a filter to the events being reported, if you reach out to me I'll give you all the info I've got about it and show you how far I've got with it as well, we might be able to work it out ;)

Because I want to keep this all as a single executable, I will be embedding this driver inside of the executable, so when it needs to be used it'll be unpacked and then KDU will load it to the kernel.

I won't go into detail about the rest of the code as its mostly KDU and interacting with the driver from user mode, but if you're interested you can find it here .

So does it work?

Yep :) Well on everything I've tested it on, if you find something its doesn't work on or any general bugs let me know and I'll try to fix them. Also, I'm not a programmer so my code is going to be far from perfect but feel free to make any pull requests with any cool features you can think of!

Here's some examples of it running and its various features.

Loading the driver and setting the hook

Universally Evading Sysmon and ETW

Enabling the hook (disabling all logging)

Universally Evading Sysmon and ETW

Getting the status of the hook

Universally Evading Sysmon and ETW

Disabling the hook (enabling all logging)

Universally Evading Sysmon and ETW

If you still reading then thanks for sticking around, you can get updates about new projects and any other stuff I'm up to on my twitter .


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

算法设计与分析

算法设计与分析

陈慧南 / 电子工业出版社 / 2006-5 / 26.80元

《算法设计与分析:C++语言描述》内容分为3部分:算法和算法分析、算法设计策略及求解困难问题。第1部分介绍问题求解方法、算法复杂度和分析、递归算法和递推关系;第2部分讨论常用的算法设计策略:基本搜索和遍历方法、分治法、贪心法、动态规划法、回溯法和分枝限界法;第3部分介绍NP完全问题、随机算法、近似算法和密码算法。书中还介绍了两种新的数据结构:跳表和伸展树,以及它们特定的算法分析方法,并对现代密码学......一起来看看 《算法设计与分析》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码