通过对Windows API进行Unhooking绕过Cylance及其他AV/EDR

2021-01-05 6,048

原文地址:https://www.ired.team/offensive-security/defense-evasion/bypassing-cylance-and-other-avs-edrs-by-unhooking-windows-apis

 

概述


如果您尝试从运行CylancePROTECT的机器上转储lsass.exe进程内存的话,您就会发现,这是一件非常困难的事情。

 

在本实验中,我们将为读者演示如何顺利转储进程的内存并绕过Cylance(或任何其他防病毒/端点检测和响应解决方案),后者使用用户态API hooking来确定程序在执行过程中是否有恶意行为。

 

实际上,Hooking是一种古老的技术,我之前早有耳闻,但从来没有机会用过,直到我偶然发现Hoang Bui的一篇文章——该文章介绍了EDR的unhooking处理,具体地址为https://medium.com/@fsx30/bypass-edrs-memory-protection-introduction-to-hooking-2efb21acffd6。

 

虽然本实验演示的API Unhooking技术是在MiniDumpWriteDump API上下文中进行的,但是该技术同样适用于其他任何已hooked的API。

 

什么是hooking


实际上,我们可以将API hooking与网络代理进行类比:您的应用程序进行的所有API调用(包括其参数),例如CreateFile、ReadFile、OpenProcess等均会被AV/EDR拦截并进行相应的检查,然后由AV/EDR裁定程序的操作/意图是否是恶意的。


如何进行hooking


对于EDR厂商来说,他们对用户态API的hook方法是:劫持/修改Windows DLL(如kernel32/kernelbase和ntdll)中的函数定义(API)。

 

一般来说,函数定义是通过在其开头插入一个jmp指令来进行修改的。这些jmp指令将改变程序的执行流程——程序将被重定向到EDR的检查模块,该模块将判断程序是否表现出任何可疑的行为,具体来说,它们是通过分析传递给EDR正在hooking/监视的函数的参数来进行评估的。这种重定向有时被称为绕行/蹦床。

 

希望下面的图表有助于进一步阐明这一个过程:

 

1.png

 

值得注意的是,并非所有的函数都会被AV/EDR劫持。通常只有那些在已知恶意软件中被经常滥用的函数才会被hooked,这些函数包括CreareRemoteThread、NtQueueApcThread等。

 

实验过程

 

先让Cylance逮个正着

 

请注意,这个实验实际上是建立在另一个实验的基础之上的。在那个实验中,我编写了一个小型的C++程序,使用MiniDumpWriteDump Windows API转储lsass.exe进程内存,详情请访问https://www.ired.team/offensive-security/credential-access-and-credential-dumping/dumping-lsass-passwords-without-mimikatz-minidumpwritedump-av-signature-bypass。

 

现在,让我们尝试在一个被CylancePROTECT监控的系统上运行这段代码(在上面的实验中编写的程序)。这时,该程序会因违规而被终止,相关消息为LsassReadstraight away:

 

1.png

 

正如您猜到的那样,Cylance hook了MiniDumpWriteDump的API调用。更准确地说,它实际上是hook了一个NtReadVirtualMemory函数,该函数来自ntdll.dll,而它是由MiniDumpWriteDump函数在幕后进行调用的。

 

确认Hook

 

用调试器执行该程序,我们可以观察到,在进程开始运行时,Cylance的内存保护模块,即CyMemDef64.dll被注入到了Invok-CreateMemoryDump.exe(我们的程序,它会调用MiniDumpWriteDump函数)进程中。这个模块将对Invok-CreateMemoryDump.exe中的API调用进行相应的检查。

 

1.png

 

既然我们知道MiniDumpWriteDump调用了NtReadVirtualMemory,那么不妨检查一下NtReadVirtualMemory的函数定义,看看是否有可疑之处。

 

@WinDBG
u NtReadVirtualMemory

 

我们立即看到,该函数的第一条指令是指向某个奇怪内存地址的JMP指令,该地址超出了ntdll模块的内存地址范围:

 

1.png

 

下面,让我们反汇编一下该地址:

 
@WinDBG
u 0000000047980084

 

我们可以立即注意到,还有多个指向Cylance内存保护模块CyMemDef64.dll的jmp指令——这证实了函数NtReadVirtualMemory已被hooked:

 

1.png

 

为了确认我们的程序最终将调用NtReadVirtualMemory,我们可以在其上放置一个断点并继续执行程序。如以下屏幕截图所示,这里命中了该断点:

 

1.png

 

如果我们继续执行程序,它将被重定向(jmp指令)到Cylance的内存保护模块,并且该程序将随着Violation:LsassRead消息的到来而崩溃。

 

Unhooking处理

 

为了进行unhook,换句话说,将被hooked的函数还原到其初始状态,我们需要知道它在被Cylance修改之前是什么样子的。

 

通过检查函数NtReadVirtualMemory的前5个字节,可以很容易做到这一点;在加载到内存之前,该函数位于c:\windows\system32\ntdll.dll库中。我们可以在ntdll的DLL导出表中看到该函数的相对虚拟地址(RVA)——在我们的实验中,该地址为00069C70(在您的系统上,该地址可能会有所不同):

 

1.png

 

如果将RVA转换为物理文件位置(由于文件尚未加载到内存中,因此与RVA相同),我们可以看到该函数的前5个字节为4c 8b d1 b8 c3:

 

1.png

 

上面的意思是,如果我们将Cylance注入的NtReadVirtualMemory函数的前5个字节(e9 0f 64 f8 cf)替换为4c 8b d1 b8 3c,那么,Cylance将会“失明”,从而无法监视MiniDumpWriteDump API调用。

 

有了这些信息,我们可以更新程序,并让它找到函数NtReadVirtualMemory的地址,并通过将字节4c 8b d1 b8 3c写入该函数的开头部分来实现unhook,具体如下面的第17行代码所示:

 

1.png

 

重新编译并再次运行该程序将成功转储lsass.exe进程内存,而不会受到Cylance的干扰:

 

1.png

 

我们现在可以令转储文件offline并将其加载到mimikatz中……。

 

在这里,我们只对一个函数进行了unhook处理;对于其他函数,通过比较磁盘上的DLL文件中的函数定义和内存中的函数定义,如果跟内存中的函数定义不同,则意味着它被“钩住”了,这时,我们可以用磁盘上的定义中找到的指令进行覆盖即可。

 

参考资料

 

下面是很好的参考资料,其中包括Cylance对unhooking技术的描述:

 

 

(请在此处插入演示视频)


本文作者:mssp299

本文为安全脉搏专栏作者发布,转载请注明:https://www.secpulse.com/archives/150747.html

Tags:
评论  (0)
快来写下你的想法吧!

mssp299

文章数:51 积分: 662

安全问答社区

安全问答社区

脉搏官方公众号

脉搏公众号