首页
社区
课程
招聘
[翻译]使用hook绕过EDR内存保护
发表于: 2020-6-1 19:36 18371

[翻译]使用hook绕过EDR内存保护

2020-6-1 19:36
18371

在最近的一次渗透测试中,我需要对抗某EDR产品,这个产品使我难以访问lsass的内存,也就不能使用自定义的Mimikatz来dump明文密码。

作为一个前恶意软件作者—我知道好几种在驱动中实现该EDR产品中这一功能的方法。我首先想到的第一种方法是Obregistercallback,它通常用在反病毒产品中。微软实现了这一回调,因为许多反病毒产品对winapi的hook非常简陋,从而使得恶意软件能够实现rootkits。然而,在msdn页面的最下方,有一段文字:“从_Windows Vista 的Service Pack 1(SP1)和Windows Server 2008 开始可用。”为了利用某些环境相关的功能,我当时使用的是Windows Server 2003,因此,要采用这一方案缺少必要的函数。

在又花了好几个小时之后,通过修改csrss.exe的magic字段,并通过csrss.exe继承lsass.exe的句柄,我成功地拿到了具有PROCESS_ALL_ACCESS访问权限的lsass.exe句柄。这种方式通过利用csrss生成一个子进程,来继承lsass当前的句柄。

然而,当我准备享受攻破一个EDR产品的喜悦时,我得出一个令人失望的结论。EDR仍然阻止我把shellcode注入进csrss中,以及通过RtlCreateUserThread创建线程。然而,出于某些原因——虽然不能生成子进程并继承其句柄,但不知道怎么回事,我仍然能获取到lsass.exe的PROCESS_ALL_ACCESS权限的句柄。

什么?还有这种事?

别着急,我不抱任何期待地用下面这条语句试着打开lsass.exe的句柄:
HANDLE hProc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, lsasspid);
你知道发生了什么吗?我获取了对lsass.exe的FULL CONTROL权限。EDR甚至没有做一点点简单的混淆。此时我意识到,我从一开始就采用了错误的方式,EDR并不在乎你获取句柄的访问权限,它在意的是你拿到句柄之后做了些什么。

现在我们已经知道了要获取lsass.exe句柄的完全控制权限并不困难,我们接下来需要考虑下一个问题。在这个句柄上调用MiniDumpWriteDump()失败了。

接下为我们进一步分析这条警告:“Violation: LsassRead”。我没看懂什么意思,他到底想表达什么?我只是想要dump进程。然而,我也知道要dump一个远程进程,必然要调用某些WINAPI,比如 MiniDumpWriteDump()中的ReadProcessMemory (RPM)。我们来看一下ReactOSMiniDumpWriteDump()的源代码。

如你所见,函数(2)dump_exception_info(),以及一些其他函数,依赖(3)RPM来实现它的功能。这些函数会被MiniDumpWriteDump (1)引用,这可能就是我们这一问题的根源。到这里就到了经验发挥作用的时候了。你必须理解Windows System Internal以及WINAPIs是怎样被处理的。拿ReadProcessMemory来举例,它工作机制是这样的。
ReadProcessMemory只是一层封闭。它会进行一系列正确性检查,比如空指针检查。这就是RPM的所有功能。然而,RPM也会调用“NtReadVirtualMemory”函数,它会在执行syscall之前设置寄存器。syscall仅仅是告诉CPU进入内核模式,接下来另一个函数ALSO(也就是NtReadVirtualMemory)会被调用。它的功能逻辑才是ReadProcessMemory的核心逻辑。
— — — — — -Userland — — — —-— — | — — — Kernel Land — — —RPM — > NtReadVirtualMemory --> SYSCALL->NtReadVirtualMemoryKernel32 — — -ntdll — — — — — — — — — - — — — — — ntoskrnl

在掌握上述知识后,我们就能理解EDR产品是怎么检测并拦截RPM/NtReadVirtualMemory调用的。答案很简单,就是hook。你可以在我之前的文章中找到关于hook的更多信息。简而言之,它使你能够把你的代码放到任意一个函数中间,并获取参数和返回值的访问权限。我100%确定EDR是用了我之前提到过的一种或几种hook技术。

然而,读者该知道的是,并不是所有的EDR产品都会运行一个service,尤其是运行在内核态的驱动。因为拥有内核态的访问权限,驱动可以在RPM的调用栈中执行任意级别的hook。然而,这也会导致Windows环境中的巨大的安全隐患。因此,一种解决方案是预先预防这种修改,也就是Kernel Patch Protection(KPP或Patch Guard)。KPP会扫描内核中每一个层级,如果检测到修改,就会触发BSOD。其中也包括ntoskrnl部分,它封闭了WINAPI的内核级的逻辑。基于这些知识,我们可以假设EDR不会也不能hook调用栈中任何内核级的函数,只会操作用户态的RPM和NtReadVirtualMemory调用。

要看函数位于我们应用内存的哪个位置,可以使用printf,将%p作为格式化字符串,函数名作为参数。如下所示:

然而,不像RPM,NtReadVirtualMemory并不是ntdll中的一个导出函数,因此你不能直接引用这个函数。你首先要指定函数的签名以及将ntdll.lib链接进你的项目中。

当一切就续之后,我们让它跑起来看看结果~

现在,我们拿到了RPM和ntReadVirtualMemory的地址。接下来我用我最喜欢的逆向工具Cheat Engine来读取内存并分析它的结构

对于RPM函数,看起来一切运行良好。它进行了一些栈操作并设置寄存器,然后调用Kernelbase(下次再详细介绍)中的ReadProcessMemory()。它最终会调用到ntdll的NtReadVirtualMemory。然而,如果你看一看NtReadVirtualMemory,并对最基本的hook框架detour有所了解,你马上能看出这一块指令是不正常的。函数的前5个字节被修改了,其他的没变。你可以看看其他相似的函数来识别这个。其他的函数都是类似这样的格式:

区别在于syscall的id,(用于在内核态区分WINAPI函数)。然而,NtReadVirtualMemory中第一条指令是一条跳转到其他地址的JMP指令,让我们跟下去看看。

ok,现在我们不在ntdll模块中了,而是进入到了CyMemdef64.dll模块。现在我懂了。

EDR放了一个跳转指令到NtReadVirtualMemory函数的开始位置,把代码流转向了他自己的模块,然后检查恶意行为,如果检查失败,会返回给Nt*函数一个错误码,不再进行内核态执行。

现在我们已经很清楚EDR是怎么检测并拦截我们的WINAPI调用的了。我们应该怎么办呢?有两个解决方法。

我们已经知道了NtReadVirtualMemory函数原来的实现,我们可以把jmp指令修改回它原本的指令。这样我们的操作就不会再被CyMemDef64.dll拦截,可以毫无阻拦地进入他们控制不到的内核态。

我们也可以创建一个我们自己的函数,和上一种方法类似,但是我们不会修改被hook的函数,我们会重新在另一个地方创建它。然后,修改Ntdll的IAT表,把NtReadVirtualMemory的地址和fixed_NtReadVirtualMemory的地址互换。这一方式的好处是如果EDR会检查它的hook有没有被修改,它就检测不出来。它仅仅是不会再被调用了,因为ntdll IAT指向了其他地方。

我采用了第一种方法。它很简单,这样我就能迅速完成这篇博客:)。然而,如果采用第二种方法的话,就会很繁琐,接下来的几天我打算试试。在这里我要提一下我的主管Andrew,他现在在医院做阑尾割除手术。希望他快点好起来。

这一方法当前仅对这一EDR有效,然而,要逆向其他的EDR产品并写出一种通用的绕过方式也并不算困难,因为它在hook时会有各种各样的限制(感谢KPP)


[注意]APP应用上架合规检测服务,协助应用顺利上架!

收藏
免费 4
支持
分享
最新回复 (4)
雪    币: 248
活跃值: (1121)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
[这种方式通过利用csrss生成一个子进程,来继承lsass当前的句柄。]
赞!
先傍大款,再当干儿子,再继承大款的权限、资源,干一些想干而干不成的事。的确妙!
2020-6-2 00:54
0
雪    币: 174
活跃值: (181)
能力值: ( LV3,RANK:20 )
在线值:
发帖
回帖
粉丝
3
写怎么多没人看吗,好文.
2020-6-2 08:58
0
雪    币: 284
活跃值: (3694)
能力值: ( LV5,RANK:75 )
在线值:
发帖
回帖
粉丝
4
好像全文精简成一句话,就是 CyMemDef64.dll hook了 NtReadVirtualMemory
2020-6-2 18:07
0
雪    币: 1555
活跃值: (3123)
能力值: ( LV11,RANK:180 )
在线值:
发帖
回帖
粉丝
5
avast 也会拦截minidump,修改注册表,让lsass主动加载自己的dll,就可以绕过了
2020-6-7 00:55
0
游客
登录 | 注册 方可回帖
返回
//