如果没有许多优秀的安全研究人员的帮助,我对 EDR 的理解是不可能的。以下是一些真正帮助我获得所需理解的文章和演讲,并为将在这里介绍的研究打下了基础。如果您有兴趣深入了解,请务必查看以下研究(排名不分先后):
Jackson T :逆转和规避 EDR 的指南
克里斯托弗·维拉:
CrikeyCon 2019 - 逆转和绕过 EDR
最近的 EDR 和 AV 观察
Matteo Malvica:让 EDR 静音。如何禁用进程、线程和图像加载检测回调
威廉伯吉斯:EDR时代的红队
BatSec:普遍规避 Sysmon 和 ETW
Rui Reis (fdiskyou) : Windows Kernel Ps 回调实验
Hoang Bui :绕过 EDR 的内存保护,Hooking 介绍
Omri Misgav 和 Udi Yavo:绕过用户模式挂钩
Ackroute : Sysmon 枚举概述
一般来说,当涉及到进攻性行动和 EDR 系统时,这些技术可以归入以下四类之一。
本文将讨论那些更属于“篡改”类别的方法,这些方法会扰乱传感器本身的内部工作以及它们如何与系统挂钩。虽然 EDR 也可以安装在 MacOS 和 Linux 上,但本文将只关注 Windows 系统。
我们将讨论以下主题:
由于对 EDR 的研究相对较新,因此这些主题通常来自独立研究人员对该技术各个方面的研究。我的目标是将其中的一些研究汇总到一个高层次的概述中,以增加对新进攻技术的一般理解、知识和驾驶检测能力。
话虽如此,本文的目的是不像某些个人研究那样深入(兔子洞*很深*),所以如果这个话题引起你的兴趣,我鼓励你查看上面的一些链接。
EDR 代表端点检测和响应。EDR 是下一代防病毒软件,可检测主机系统上的可疑活动。它们提供了持续监控和高级威胁所需的工具。
EDR 不仅可以查找恶意文件,还可以监控端点和网络事件并将其记录在数据库中,以供进一步分析、检测和调查。在许多 EDR 控制台中,您可以看到流程树、执行流程、流程注入等等。如果您曾经或现在是安全分析师,您甚至可能在工作中直接使用过这些工具。
以下是您可能知道的一些常见的 EDR 供应商。
在我们了解 EDR 的工作原理之前,我们需要对 Windows 内核有一个基本的了解。在 Windows 体系结构中,有不同的访问模式。其中两种模式是用户模式(非正式地称为用户空间)和内核模式(内核空间)。
您可以将用户区视为您与之交互的 Windows 的一部分。这包括用户可用的应用程序,例如 Microsoft Office、Internet 浏览器(Chrome、Firefox 和其他)等。通常,您将在日常工作或家庭使用中使用的任何应用程序。
内核区是系统服务运行的地方。其中包括操作系统代码、设备驱动程序、系统服务等等。内核模式有权访问所有系统内存和 CPU 指令。您可以将内核视为操作系统的心脏,它在应用程序和底层硬件之间架起了一座桥梁。
拆分的原因是为了保护关键的 Windows 功能不被用户或用户登陆应用程序修改。如果用户能够直接修改内核代码,则不仅存在巨大的安全问题,而且还存在功能问题。如果关键功能被篡改,导致未处理的异常,可能会导致严重错误并导致系统崩溃。
微软文档
在过去(x86 Windows XP 和之前的日子),用户空间和内核空间之间并没有明确的权限划分。应用程序能够为自己的目的修补内核功能(尽管微软不鼓励这种做法)。例如,应用程序可以将内核系统调用表修补到它们自己的内存空间。因此,当应用程序调用某个内核函数时,反病毒或恶意应用程序可以直接执行到它们自己的内存空间。
下面是一个潜在系统调用表的示例以及如何修改它。在 PatchGuard 之前,应用程序可以更改系统调用表地址。因此,当另一个应用程序尝试调用该函数时,它实际上会调用“流氓”函数。
当然,这并不总是出于恶意,因为许多防病毒引擎使用此功能来提高其可见性(如果恶意应用程序调用某个函数,它会被 AV 函数重定向和分析)。出于同样的原因,这为称为“rootkits”的恶意软件家族铺平了道路,这些恶意软件家族可以在内核的最深层运行,完全控制操作系统功能,甚至保留过去的系统恢复。不仅如此,任何应用程序(无论好坏)对内核做了坏补丁,并导致未处理的异常,都会导致主机蓝屏和崩溃。
对此的 Windows 解决方案是内核补丁保护 (KPP),通常称为PatchGuard。此功能首先在 x64 版本的 Windows XP 和 Server 2003 SP1 中实现。此功能强制限制内核中可以修改和不能修改的内容(例如修改系统调用地址)。如果制作了任何未经授权的补丁,PatchGuard 将执行错误检查并通过蓝屏关闭系统/重启并出现CRITICAL_STRUCTURE_CORRUPTION错误。可能触发错误检查的修改示例如下:
需要注意的是,PatchGuard 不是万能的安全子弹。由于 Windows 系统的运行方式,有绕过 PatchGuard,恶意补丁和 rootkit 仍然存在(尽管很少见)。深入了解 PatchGuard 不在我们的 EDR 研究范围内。
最重要的是,由于这些保护措施,继续使用内核补丁进行操作并不符合 AV/EDR 供应商的最佳利益。因为:
正因为如此,如果应用程序(包括 AV/EDR 应用程序)想要重定向执行流程,它必须发生在用户空间内。它们确实通过驱动程序之类的东西与内核交互,但我们将很快讨论。
当应用程序调用 Windows API 来执行其代码时,流程看起来像这样。(此特定图形来自 Christopher Vela 的 2019 CrikeyCon 演讲)。当然,这是针对Mimikatz.exe的,但对于任何程序都可以这样说。
来自 Christopher Vella 的幻灯片
在这个例子中,因为 Mimikatz 要从 LSASS 中读取内存,它必须调用 Kernel32 库中的ReadProcessMemory函数(由 Kernel32.dll 调用)。函数调用最终将被转发到 Ntdll.dll 中的NtReadVirtualMemory调用。(内部工作稍微复杂一些,但对于本示例的目的,了解这些 Windows API 调用如何工作并不是完全必要的)。
由于应用程序不能直接与内核交互,它们使用所谓的“系统调用”。系统调用充当对内核领域的类似代理的调用。
基本上,NTDLL 创建内核的系统调用,内核将在其中执行NtReadVirtualMemory的系统调用。内核运行必要的函数,并且该函数的结果从系统调用返回给应用程序。这样,应用程序能够利用内核功能,而无需实际修改或在内核内存空间中运行
在某些情况下,应用程序需要访问内核中受保护的数据。为此,使用了相应的驱动程序。有不同类型的驱动程序,例如硬件和软件驱动程序。对于本文的重点,我们将关注软件驱动程序,因为我们不会与打印机等硬件进行交互。
根据Microsoft 文档,当工具需要访问核心操作系统数据结构时,会使用软件驱动程序。只能由在内核模式下运行的代码访问的数据结构类型。通常,需要此功能的工具分为两部分:
下面是来自同一 Microsoft 文档的图形:
在许多 EDR 实现中,有一个软件驱动程序允许应用程序访问内核并利用它来提高对进程的可见性。
重要的是要知道,虽然驱动程序可以在内核模式下运行,但它们仍然受到 PatchGuard 限制。他们无法在不使系统崩溃的情况下修补受保护的内存。
当 Microsoft 实施 PatchGuard 时,据了解这将删除某些程序(例如 AV)的功能。实现的妥协是使用所谓的内核回调。
内核回调的工作方式是驱动程序可以在其代码中为任何受支持的操作注册“回调”,并在执行该特定操作时接收前或**后通知。**回调不会对底层 Windows 内核进行任何修改。
这些回调的常见实现是PsSetCreateProcessNotifyRoutine(Ex)。当驱动程序实现此回调函数时,只要创建新进程,就会调用此回调例程并向请求它的驱动程序发送通知。然后驱动程序可以相应地执行它自己的功能。
请记住,这可以是前置或后置通知。如果安全设备收到正在创建进程的预先通知,它可以检查这是否是已知的坏文件,并向 EDR 驱动程序发出通知以防止该进程发生。同样,如果风险未知,它可以采取发布通知并记录过程动作,以便以后进一步分析和关联。
我能找到的最简单的视觉效果来自OpenSourceForU。当某个动作发生时,回调将通知发送到指定的内核驱动程序,然后将指令发送回用户态应用程序。
所以,我知道先决条件部分有很多信息,您可能会问为什么需要了解 Windows 内核才能理解 EDR。这是理解 EDR 的基础,因为它们涉及上述所有主题。
为了获得他们的可见性,EDR 执行某些版本的以下操作。
对于接下来的部分,请记住我们之前的流程树。在将 EDR 添加到混合中时,我们将详细说明此流程树。
来自 Christopher Vella 的幻灯片
如前所述,许多 EDR 应用程序都有相应的驱动程序来实现内核回调。此示例将在“进程创建”回调中。执行应用程序(如 Mimikatz.exe)时,需要通过“ CreateProcessW ”等函数创建进程。通过调用此函数,会触发相应的回调函数,并且任何实现该回调的驱动程序都会收到通知。因此,在我们的下图中:
Christopher Vella 修改后的幻灯片
现在让我们讨论一下 NTDLL 挂钩的含义。
查看我们的 Mimikatz 图表,这是我们目前正在执行的地方。收到回调通知后,按照驱动程序的指示,NTDLL 已被 EDR 应用程序挂钩。通过挂钩 NTDLL,执行流程被重定向到 EDR 内存空间和函数(例如 DLL)。由于它是在用户空间修补内存空间,因此不存在内核崩溃的风险,并且符合 PatchGuard。
来自 Christopher Vella 的幻灯片
现在你可能想知道钩子是什么意思。进一步细分,下面是 EDR 如何挂钩 DLL 的示例。
在原始的 NTDLL 内存空间(顶部的红色框)中,可以看到 syscall 指令将执行传递给内核。这是未挂钩函数的正常流程。
在 hooked/patched 函数(底部)中,可以看到 EDR 内存空间的无条件跳转(或其他指令),在此图中为ctiuser(在我们的图表范围内,这是 EDR.dll)。
重定向执行流后,EDR 引擎会分析请求并确定可以运行的执行。如果确定执行足够安全,可以运行,它会将函数重定向回原始的NtWriteVirtualMemory地址并执行系统调用到内核并将响应返回给请求的应用程序。(左流)
如果判断为恶意调用,则不进行系统调用,终止进程。(右流)
Christopher Vella 修改后的幻灯片
回到我们的 Mimikatz 图,这是我们的流程,包括回调和挂钩。
Christopher Vella 修改后的幻灯片
好的,既然我们对 EDR 设备如何获得可见性有了基本的了解,我们就可以开始了解它们的弱点了。据我们目前所知,我们有两个主要的地方可以阻碍执行流程
虽然删除 DLL 挂钩会起作用,但它可能必须从我们运行的每个可执行文件中取消挂钩。这并非不可能,但我们要偷懒,走阻力最小的道路。如果我们完全删除内核回调,理论上,我们运行的任何可执行文件都不会受到 EDR 的判断。这比对每个可执行文件更具选择性和执行它更隐蔽,但我们不会专注于本演示的 DLL 挂钩。
查看我们的图表,这里是我们要使传感器失明的地方(蓝色)。
Christopher Vella 修改后的幻灯片
如果不进行回调,EDR 驱动程序将不知道将发送到内核的函数调用,EDR 设备将永远不会指示挂钩 DLL,并且在执行流程中不会发生重定向。因此,返回一个干净的、不受监控的流:
来自 Christopher Vella 的幻灯片
要删除回调,我们可以从三个选项中选择一个(尽管我相信您可以想出更多),具体取决于我们想要的破坏性。
让我们分解一下。
回调数组有很多内容,但为了简单起见,您可以将其视为一个数组,其中包含指向从回调函数请求通知的每个驱动程序的指针。为了说明这一点,我将进入 Windows 内核调试器 (KD)。我们不会详细介绍调试的工作原理;这更多地表明实际上存在一个回调数组。
首先,我们将反汇编(“u”)PspSetCreateProcessNotifyRoutine,我们现在只需要知道这是在创建新进程时运行的回调例程。我们将继续反汇编,直到达到“lea”指令。同样,您需要知道的是,此地址将保存包含请求回调的驱动程序列表的回调数组。
查看这个内存地址,我们看到以下数组。以红色突出显示的所有内容都是指向驱动程序的不同指针。
现在,我将稍微作弊并使用我们稍后将讨论的工具,但为了证明这些是驱动程序回调,让我们将它们与它们的名称一起列出。虽然我不会提及这是哪个特定的 EDR,但请相信突出显示的是 EDR 驱动程序。
我们可以将这个数组中的每个地址都归零,但这可能会导致其他驱动程序可能出现不当行为,而作为对手或红队队员,您可能不希望这样做。
正如我们在上面看到的,我们数组中第 6 个元素的值是我们的 EDR 驱动程序。如果我们将数组中第 6 个元素的回调地址清零(第 7 个值,因为数组从 0 开始),理论上,我们应该能够使 EDR 不知道进程创建事件。
为了演示,让我们在不修改任何回调的情况下运行 Mimikatz(GitHub 上的最新版本,无需修改)。通过运行它,我们正在调用“进程创建”函数并触发一个回调,该回调将通知 EDR,因为它的驱动程序位于回调数组中。
我们看到驱动程序看到了正在创建的恶意进程并指示终止该进程。
现在,让我们将 EDR 回调归零,从数组中删除 EDR 驱动程序,看看我们是否可以停止向 EDR 应用程序发送通知。
运行 Mimikatz。由于不再有回调通知,EDR 不知道进程已创建,因此不执行分析/终止。
如果我们将驱动程序地址返回到我们的回调数组,我们可以在运行程序时看到 EDR 按预期运行。
此方法涉及将 EDR 驱动程序回调保留在数组中(不归零),但将函数中的第一条指令更改为“ret”函数。在汇编指令中,这基本上意味着只是返回。
进一步反汇编 EDR 驱动程序函数,我们可以看到任何更改之前的开始指令。
再次使用我们的秘密工具,我们将使用“ret”命令修补第一条指令。
现在,当我们运行 Mimikatz 时,回调函数仍然会被调用,但它会立即“返回”到正常的执行流程:
为了证明这是可行的,让我们将原始指令返回到函数中:
我们可以看到 EDR 再次能够终止执行流程。
虽然我们可以演示使用 Windows 内核调试器使 EDR 失明,但显然,这对于红队活动或隐蔽的进攻行动来说并不理想。在您想要篡改 EDR 的每台主机上跳入调试器既不隐秘也不有效。这就是我们的秘密工具发挥作用的地方。
要通过恶意应用程序自动执行此操作,我们需要创建自己的恶意驱动程序/恶意应用程序组合,就像 EDR 驱动程序和应用程序一起工作一样。基本上,内核与内核作斗争。
我不是内核程序员,也不会假装是,所以我们将使用 fdiskyou 的 GitHub 项目中的 evil client/evil 驱动程序:
这是他研究的随附 repo ,在致谢中列出。
编译源代码,你会得到两个文件:
该应用程序与驱动程序相切。驱动程序具有读取和修改回调数组所需的权限,因为它在内核空间中运行。该应用程序是用户面板,用于指示驱动程序执行哪些命令。
要在 Windows 系统上加载驱动程序,您需要一定的权限集,并遵守一定的安全规则:
看看我们的要求,本地管理员是一个障碍,但在进攻性交战中并不少见。
加载驱动程序是我们遇到更多困难的地方。我在内核驱动程序开发方面没有经验,所以我不会选择这个选项。剩下的就是为我们的 evil.sys 驱动程序获取签名。从微软获得证书的过程变得越来越严格,(这是一件好事)并且需要微软的驱动程序审查、他们的证书和数百美元。这样就可以找到现有的证书。
如下所示,我们无法在“测试签名模式”之外加载我们的驱动程序(此处未介绍)。
在经历了一个长长的兔子洞之后,我发现了一个熟悉开发和创建自己的驱动程序的社区。令我惊讶的是,在反作弊引擎方面,视频游戏黑客遇到了与我们和 EDR 非常相似的问题。
电子游戏的反作弊引擎在功能上与 EDR 有点相似。它们通常带有一个驱动程序,该驱动程序具有注入视频游戏内存空间的相同能力,以确保没有修改任何内存或函数调用。
为了绕过这些反作弊引擎,这些黑客还会加载他们自己的驱动程序或利用现有驱动程序来禁用引擎的功能,就像我们使用 EDR 一样。(如果您有兴趣,请查看著名的易受攻击的 Capcom 驱动程序)
*在我们继续之前,我想强调一下,我不鼓励将以下技术用于恶意目的,例如未经授权的黑客攻击或在线游戏中的作弊。这只是关于如何在您有权测试的环境中滥用它们的概念证明。*
通过一些论坛挖掘,我很快找到了可能对我的问题集有答案的人。
查看证书,它甚至是在我们 2015 年 7 月 29 日截止日期之前创建的!关于驱动程序证书的另一个有趣的事实是,Microsoft 通常不关心证书是否过期。只要它在某一时刻有效。这在未来可能会改变,但现在这是允许的。
Microsoft 允许使用其SignTool和适当的交叉证书对驱动程序进行签名。交叉证书是“由一个证书颁发机构 (CA) 颁发的数字证书,用于签署另一个证书颁发机构的根证书的公钥。交叉证书提供了一种方法来创建从单个受信任的根 CA 到多个其他 CA 的信任链”
交叉证书:
Microsoft 的官方文档页面有每个 CA 交叉证书的下载。
由于我们的证书是由“VeriSign Class 3 Public Primary Certification Authority”颁发的,我们将下载相应的证书。一起使用证书和交叉证书,我们可以签署我们的邪恶驱动程序。
要签署证书,我们将使用前面提到的 SignTool。
如我们所见,我们遇到了一个小问题。这是说我们没有符合标准的证书。请记住,证书于 2014 年 11 月到期。事实证明,我们可以利用系统时间进行一些欺骗。
有了“有效”证书,我们现在应该可以毫无问题地加载驱动程序了
当我们运行相应的 evilcli.exe 应用程序时,我们现在可以利用新驱动程序的强大功能。
为了显示应用程序和驱动程序之间的相关性,下面是在不启动驱动程序的情况下运行应用程序时发生的情况。
最后,让我们使用新的 evil 程序来屏蔽 EDR 驱动程序中的进程、线程和 loadimage 回调,并执行 Mimikatz 以获得完整的密码转储。
首先,我们可以看到我们的 EDR 服务正在运行(您必须再次相信我的话)。
并显示恢复 EDR 回调:
一般来说,防病毒和其他安全设备通常不会严格审查驱动程序。与典型的用户应用程序相比,它们通常受到更多的信任。因此,病毒签名可能不是检测恶意驱动程序的最可靠方法。(我的文件中没有从 EDR 检测到 AV)。
此外,许多 EDR 没有实施防篡改措施来检查其回调是否被清零或更改。造成这种情况的原因可能是因为它们在内核中运行,它们不希望因连续检查而产生额外的 CPU 周期开销。未来可能会随着新的研究而改变,但目前,我们也不能依赖 EDR 来为我们检查。
我确实发现,Windows 事件日志实际上记录了系统日志中加载驱动程序的时间。以下是合法戴尔驱动程序的正常 EventID 7045(已安装新服务)。这些事件在机器上安装了新的服务/驱动程序时发生。当您安装打印机/wifi/usb 或其他驱动程序时,您可能会看到这一点。
除了一种边缘情况(至少在我的机器上)之外,当加载内核模式驱动程序时,安全标识符(SID)总是“S-1-5-18”(本地系统帐户)。
正如您在加载我们的邪恶驱动程序时看到的那样,它是由用户 SID 安装的:
诚然,驱动程序是“evil.sys”并安装在用户桌面,但在实际活动中,它可能具有合法名称并安装在*System32\Drivers*目录中。
我敢肯定,如果您正在创建具有 SYSTEM 权限的服务,它看起来会有所不同,但通常这需要像 PSEXEC 或漏洞利用这样的工具,这些工具可能会更嘈杂并且更有可能被 AV/EDR 标记。
这可能不是一个完美的检测,因为存在边缘情况。下面是 Wireshark 安装附带的 Npcap 数据包驱动程序的安装。但我可以想象,在非技术业务环境中,像这样的驱动程序可能不会安装在普通工作站上。