一
致谢
我的对于EDR的理解离不开众多优秀的安全研究人员的帮助。以下是一些非常有帮助的文章和演讲,它们帮助我获得了所需的理解,并让我在即将呈现的研究中能够快速上手。如果你有兴趣深入了解,一定要查看以下研究(无特定顺序):
二
简介
一般而言,涉及攻击行动和EDR系统时,技术可以分为以下四类:
1.避免使用这些技术
◆如果主机上安装了EDR,就转向没有安装该设备的主机。
◆通过主机代理流量,以避免在系统上执行命令。
2.遵循灰色地带的操作
◆与典型的网络流量融合在一起。这些行为可能稍微可疑,但保持足够低调,需要人工分析进一步了解。
3.在盲点中操作
◆坚持那些可能不会被记录的技术。(例如,API,移除用户态钩子,创建自己的系统调用)
4.禁用/干扰EDR传感器
◆修补传感器、重定向流量、卸载等。
本文将讨论那些更多属于“干扰”类别的方法,即干扰传感器自身的内部工作方式以及其如何hook系统。尽管EDR也可以安装在MacOS和Linux上,但本文仅专注于Windows系统。
我们将讨论以下主题:
◆Windows内核简要概述
◆安全设备中的内核回调
◆EDR的工作原理和原因
◆消除EDR可见性的技术
◆其他有趣主题的简要见解
视频游戏黑客社区
Windows rootkit(根工具包)
由于EDR研究相对较新,这些主题通常来自独立研究人员对技术的个别方面的研究。我的目标是在高层次概述中汇集部分这些研究,以增加对技术的一般理解、了解新的攻击技术,并推动检测能力的发展。
话虽如此,本文的目的并不是像一些个别研究那样深入探讨(兔子洞非常深)。因此,如果这个主题引起了你的兴趣,我鼓励你查阅上面提到的一些链接。那些链接将为你提供更深入的了解。
三
什么是EDR?
EDR代表终端检测与响应(Endpoint Detection and Response)。EDR是下一代反病毒软件,用于检测主机系统上的可疑活动。它们提供了持续监控和高级威胁所需的工具。
EDR不仅可以查找恶意文件,还可以监视终端和网络事件,并将它们记录在数据库中以供进一步分析、检测和调查。在许多EDR控制台中,你可以查看进程树、执行流程、进程注入等等。如果你曾经或目前是安全分析师,你可能甚至直接在工作中使用过这些工具。
以下是一些你可能熟悉的常见EDR供应商:
四
先决条件
在我们深入了解EDR的工作原理之前,我们需要对Windows内核有一个基本的了解。在Windows架构中,有不同的访问模式。其中两种模式分别是用户模式(非正式称为用户态)和内核模式(内核态)。
你可以将用户态视为与之交互的Windows部分。这包括用户可使用的应用程序,如Microsoft Office、互联网浏览器(Chrome、Firefox等)。通常情况下,这包括你在日常工作或家庭使用中使用的任何应用程序。
内核态是系统服务运行的地方。这包括操作系统代码、设备驱动程序、系统服务等等。内核模式具有对所有系统内存和CPU指令的权限。你可以将内核视为操作系统的核心,它连接应用程序和底层硬件之间的桥梁。
将用户态和内核态分开的原因是保护关键的Windows功能,使其无法被用户或用户态应用程序修改。如果用户能够直接修改内核代码,不仅会带来严重的安全问题,还会引起功能问题。如果关键功能被篡改,导致未处理的异常,可能会引发严重错误并导致系统崩溃。
在过去(即x86 Windows XP时代及之前),用户态和内核态之间并没有明确的权限分隔。应用程序可以为自己的目的修改内核函数(尽管这种做法受到微软的反对)。例如,应用程序可以将内核系统调用表(syscall table)的地址修改为自己的内存空间。因此,当一个应用程序调用特定的内核函数时,反病毒软件或恶意应用程序可以将执行重定向到它们自己的内存空间。
下面是一个潜在的系统调用表以及如何进行修改的示例。在引入PatchGuard之前,应用程序可以更改系统调用表中的地址。因此,当另一个应用程序尝试调用该函数时,实际上是调用了"rogue"函数。
诚然,并非所有利用内核补丁的应用程序都具有恶意意图。许多反病毒引擎利用这种功能来增加它们的可见性(如果恶意应用程序调用某个函数,它将被重定向并由反病毒函数进行分析)。然而,同样的道理也为被称为"rootkit"的恶意软件家族铺平了道路。这些rootkit能够在内核的最深层级上运行,完全控制操作系统功能,甚至在系统恢复后仍然存在。不仅如此,任何一个应用程序(无论是好的还是坏的)如果对内核进行错误的补丁,导致未处理的异常,都会导致主机出现蓝屏和崩溃的情况。
Windows的解决方案是内核补丁保护(Kernel Patch Protection,KPP),通常被称为PatchGuard。这个功能最初是在Windows XP和Server 2003 SP1的x64版本中实现的。该功能限制了在内核中可以修改和不能修改的内容(比如修改系统调用地址)。如果进行了任何未经授权的补丁,PatchGuard将执行一个错误检查(bug check),通过蓝屏/重启并显示CRITICAL_STRUCTURE_CORRUPTION错误来关闭系统。可能会触发错误检查的修改示例如下:
需要注意的是,PatchGuard并不是万能的安全防护措施。由于Windows系统的运作方式,存在绕过PatchGuard的方法,恶意补丁和rootkit仍然存在(尽管比较罕见)。深入研究PatchGuard超出了我们EDR研究的范围。
最重要的一点是,由于这些保护措施,AV/EDR供应商继续使用内核补丁来运行并不符合他们的最佳利益,原因如下:
因此,如果应用程序(包括AV/EDR应用程序)希望重定向执行流程,这必须在用户空间内完成。它们可以通过驱动程序等方式与内核进行交互,但我们会很快详细介绍这一点。
当一个应用程序调用Windows API来执行其代码时,执行流程大致如下所示。(这个特定的图形来自Christopher Vela在2019年CrikeyCon演讲中的内容)。尽管这个图形是针对Mimikatz.exe的,但对于任何程序都是适用的。
Slide from Christopher Vella
在这个例子中,由于Mimikatz想要读取LSASS的内存,它必须调用Kernel32库中的ReadProcessMemory函数(由Kernel32.dll调用)。该函数调用最终将被转发到Ntdll.dll中的NtReadVirtualMemory调用。(内部工作原理可能更加复杂,但就本例而言,完全了解这些Windows API调用的工作原理并不是必需的。)
由于应用程序无法直接与内核交互,它们使用了所谓的"Syscalls"(系统调用)。系统调用充当了一个类似代理的调用,用于与内核通信。
基本上,NTDLL创建了一个系统调用到内核,内核将执行NtReadVirtualMemory的系统调用。内核运行所需的函数,并将该函数的结果从系统调用返回给应用程序。通过这种方式,应用程序能够利用内核功能,而无需实际修改或在内核内存空间中运行。
在某些情况下,应用程序需要访问内核中的受保护数据。为此,需要使用相应的驱动程序。驱动程序有不同类型,例如硬件驱动程序和软件驱动程序。在本文中,我们将重点关注软件驱动程序,因为我们不会与打印机等硬件进行交互。
根据微软的文档(https://docs.microsoft.com/en-us/windows-hardware/drivers/gettingstarted/what-is-a-driver-),当工具需要访问核心操作系统数据结构时,会使用软件驱动程序。这种类型的数据结构只能被在内核模式下运行的代码访问。通常,需要这种功能的工具被分为两部分:
1.用户模式组件(应用程序)
◆该组件在用户模式下运行并提供用户界面。在EDR的范围内,可以将其视为用于分析事件的GUI控制台。
2.内核模式组件(驱动程序)
◆该组件在内核模式下运行,并将信息传递回相应的应用程序。
下面是来自同一份微软文档的图形示例:
在许多EDR实现中,存在一个软件驱动程序,使应用程序能够访问内核并利用其来提高对进程的可见性。
需要注意的是,虽然驱动程序可以在内核模式下运行,但它们仍受到PatchGuard的限制。它们不能在不崩溃系统的情况下修改受保护的内存。
当微软实施PatchGuard时,人们意识到这将使一些程序(如反病毒软件)失去功能。为了解决这个问题,实施了所谓的内核回调机制。
内核回调的工作方式是,驱动程序可以在其代码中注册一个回调函数,用于任何支持的操作,并在执行该特定操作时接收预先或后续通知。回调函数不会对底层的Windows内核进行任何修改。
这些回调的常见实现是PsSetCreateProcessNotifyRoutine(Ex)。当驱动程序实现了这个回调函数时,每当创建一个新进程时,就会调用这个回调例程,并向请求它的驱动程序发送通知。驱动程序随后可以相应地执行自己的功能。
请记住,这可以是预先通知或后续通知。如果安全设备接收到新进程正在创建的预先通知,它可以检查该文件是否是已知的恶意文件,并向EDR驱动程序发送通知,以防止进程的发生。类似地,如果风险是未知的,它可以接收后续通知,并记录进程操作以供以后进行进一步的分析和关联。
我找到的最简单的图形示例来自OpenSourceForU。当发生某个特定操作时,回调将向指定的内核驱动程序发送通知,然后驱动程序将向用户空间应用程序发送指令。
五
那么,EDR如何工作的?
因此,我知道在先决条件部分有很多信息,你可能会问为什么需要了解Windows内核才能理解EDR。这对于理解EDR是至关重要的,因为EDR涉及到上述所有主题。
为了获得可见性,EDR执行以下某个版本的操作:
在接下来的部分中,请记住我们之前提到的进程树。当将EDR与其中混合时,我们将详细介绍这个进程树。
Slide from Christopher Vella
如前所述,许多EDR应用程序具有相应的驱动程序,实现了内核回调。本示例将涉及“进程创建”回调。当执行应用程序(如Mimikatz.exe)时,该进程需要通过“CreateProcessW”等函数创建。调用此函数时,将触发相应的回调函数,并且实现该回调的任何驱动程序都会收到一个通知。因此,在下面的图形中:
1.一个恶意用户或程序想要生成“malware.exe”。为此,调用CreateProcessW函数来创建新进程及其主线程。如果将其与我们的Mimikatz进程图进行比较,这就是在Kernel32步骤中进行的操作。
2.执行“进程创建”回调函数,并向EDR驱动程序发送一个预先通知,说明将要创建一个新进程。
3.EDR驱动程序指示EDR应用程序(EDR_Process.exe)在应用程序(malware.exe)的内存空间中注入和挂钩NTDLL,以将执行流重定向到自身。在Mimikatz图中,这是NTDLL部分,在系统调用之前。
Modified Slide from Christopher Vella
现在,让我们讨论一下NTDLL挂钩的内容。
看看我们的Mimikatz图,我们当前的执行状态如下。在接收到回调通知后,驱动程序指示EDR应用程序挂钩了NTDLL。通过挂钩NTDLL,执行流被重定向到EDR的内存空间和函数(如DLL)。由于它是在用户空间中对内存空间进行修补,所以不会导致内核崩溃,并且符合PatchGuard的要求。
Slide from Christopher Vella
现在你可能想知道什么是挂钩。进一步解释,下面是一个示例,说明EDR如何挂钩一个DLL。
在原始的NTDLL内存空间中(红色框中的顶部方块),可以看到syscall指令将执行传递给内核。这是未挂钩函数的正常流程。
在挂钩/修补的函数中(底部方块),可以看到一个无条件跳转(或其他指令)到EDR内存空间,在本示例中为ctiuser(在我们的图表范围内,这是EDR.dll)。
一旦执行流被重定向,EDR引擎会分析请求并确定是否可以安全执行。如果确定执行是安全的,它将重新将函数重定向到原始的NtWriteVirtualMemory地址,并执行系统调用将结果返回给请求的应用程序(左侧流程)。
如果判断调用是恶意的,它将不会进行系统调用,并终止进程(右侧流程)。
Modified Slide from Christopher Vella
回到我们的Mimikatz图,以下是包括回调和挂钩的流程:
Modified Slide from Christopher Vella
六
致盲EDR传感器
好的,现在我们对EDR设备如何获取可见性有了基本的了解,我们可以开始了解它们的弱点。根据我们目前的了解,我们有两个主要的地方可以阻碍执行流程:
虽然移除DLL挂钩是一个可行的方法,但需要针对每个可执行文件进行解钩。虽然这是可行的,但为了简单起见,我们将选择最简单的方法。理论上,如果完全移除内核回调,任何我们运行的可执行文件都不会受到EDR的判断。这种方法可能比选择性地为每个可执行文件移除挂钩更不隐蔽,但在本示例中,我们不会重点讨论DLL挂钩。
在我们的图表中,蓝色部分表示我们将禁用EDR的监控能力,有效地“使传感器失明”。
Modified Slide from Christopher Vella
如果没有回调被触发,EDR驱动程序将对将要发送到内核的函数调用毫无察觉,EDR应用程序将不会被指示挂钩DLL,执行流程也不会发生重定向。因此,返回一个干净、未经监控的执行流程:
Slide from Christopher Vella
七
停止回调
要删除回调,我们可以从三个选项中选择一个(尽管我确信您可以提出更多选项),这取决于我们想要的破坏性。
1.清零整个回调数组
2.清零特定进程的通知回调(只删除回调数组中的EDR驱动程序)
3.修改EDR进程通知回调函数
让我们逐个解释每个选项。
回调函数数组涉及许多内容,但为了简单起见,你可以将其视为一个数组,其中存储着每个请求从回调函数中接收通知的驱动程序的指针。为了说明这一点,我将进入Windows内核调试器(KD)。我们不会详细介绍调试的工作原理;这只是为了展示确实存在一个回调函数数组。
首先,我们将对PspSetCreateProcessNotifyRoutine进行反汇编("u"命令)。现在我们只需要知道这是一个在创建新进程时运行的回调例程。我们将继续反汇编,直到到达一个"lea"指令。同样,你只需要知道,这个地址将保存包含请求回调的驱动程序列表的回调函数数组。
查看这个内存地址,我们可以看到以下数组。所有以红色显示的部分都是指向不同驱动程序的指针。
现在,我会稍微作弊一下,使用我们稍后将讨论的工具,但为了证明这些是驱动程序的回调函数,让我们将它们与它们的名称一起列出来。虽然我不会提及具体是哪个EDR,但请相信我的话,高亮显示的是EDR驱动程序。
我们可以将数组中的每个地址清零,但这可能会导致其他驱动程序产生异常行为,作为对手或红队成员,你可能不希望这样做。
如上所示,数组中第6个元素的值是我们的EDR驱动程序。如果我们将数组中第6个元素(第7个值,因为数组从0开始)的回调地址清零,理论上我们应该能够使EDR在处理进程创建事件时失去响应。
为了演示,让我们在不修改任何回调函数的情况下运行Mimikatz(GitHub上的最新版本,没有进行任何修改)。通过运行它,我们将调用"process create"函数并触发一个回调,通知EDR,因为它的驱动程序位于回调函数数组中。
我们可以看到,该驱动程序检测到了恶意进程的创建,并指示终止该进程。
现在,让我们将EDR回调清零,从数组中移除EDR驱动程序,看看是否可以阻止通知发送到EDR应用程序。
运行Mimikatz。由于不再存在回调通知,EDR无法意识到进程的创建,并且不会执行任何分析或终止操作。
如果我们将驱动程序地址返回到回调数组中,当我们运行程序时,可以看到EDR按预期运行。
这种方法涉及将EDR驱动程序回调保留在回调数组中(不清零),但将函数的第一条指令修改为"ret"指令。在汇编指令中,"ret"指令基本上意味着返回。
通过进一步反汇编EDR驱动程序函数,我们可以查看在进行任何修改之前函数开头的指令。
使用我们的秘密工具,我们将再次使用“ret”命令来修补第一条指令。
现在,当我们运行Mimikatz时,回调函数仍然会被调用,但它会立即“返回”到正常的执行流程中:
为了证明这一点,让我们将原始指令重新放回函数中:
我们可以看到EDR再次能够终止执行流程:
八
优化攻击行动
尽管我们可以通过Windows内核调试器来演示对EDR进行遮蔽,但显然,这对于红队活动或隐秘的攻击行动并不理想。在希望干扰EDR的每台主机上都跳转到调试器中既不隐蔽也不有效。这就是我们的秘密工具发挥作用的地方。
要通过恶意应用程序自动执行此操作,我们需要创建自己的恶意驱动程序/恶意应用程序组合,类似于EDR驱动程序和应用程序一起工作。基本上,使用内核来对抗内核。
我不是内核程序员,也不打算假装成为内核程序员,所以我们将使用fdiskyou的GitHub项目中的恶意客户端/恶意驱动程序,该项目位于以下地址:
◆https://github.com/fdiskyou/windows-ps-callbacks-experiments
这是他的研究的伴随代码库,已在致谢中列出。
编译源代码后,您将获得两个文件:
◆evil.sys(驱动程序)
◆evilcli.exe(应用程序)—在我们之前的示例中,我将其重命名为“ninja.exe”
以下是该可执行文件中概述的功能。它可以清零回调数组,也可以使用“ret”命令修补函数指令。它还可以将任何更改还原回修补之前的状态。
该应用程序与驱动程序共同工作。驱动程序具有读取和修改回调数组所需的权限,因为它在内核空间运行。应用程序是用户界面,用于指示驱动程序执行哪些命令。
九
在Windows上加载驱动程序
在Windows系统上加载驱动程序需要一定的权限集,并遵守特定的安全规则:
1.要加载驱动程序,您需要在主机上以至少管理员权限运行。
2.Windows不允许加载未签名的内核驱动程序。
◆异常情况是如果您启用了“测试签名模式”(在非开发环境中较少见)。
◆否则,您有两个选项:
利用现有的驱动程序漏洞。
获取驱动程序的数字签名。
3.在2015年7月29日之后颁发的任何证书都不允许在某些版本的Windows 10上运行的安全启动机器上加载。
根据我们的要求,本地管理员权限是一道障碍,但在攻击性的任务中并不罕见。
加载驱动程序是我们遇到更多困难的地方。我对内核驱动程序的利用经验有限,所以我不会选择这个选项。这就只剩下获取我们的evil.sys驱动程序的数字签名。从微软获取证书的流程变得更加严格(这是一件好事),需要经过微软的驱动程序审查,颁发证书,并支付数百美元。因此,我们只能寻找现有的证书。
如下所示,我们无法在“测试签名模式”之外(本文未涉及)加载我们的驱动程序。
在深入研究过程中,我发现了一个熟悉利用和创建自己驱动程序的社区。让我惊讶的是,与我们和EDR相关的问题集与反作弊引擎在某种程度上非常相似,特别是针对视频游戏。
视频游戏的反作弊引擎在功能上与EDR相似。它们通常带有一个驱动程序,具有注入到视频游戏的内存空间的能力,以确保没有修改内存或函数调用。
为了绕过这些反作弊引擎,这些游戏黑客也会加载自己的驱动程序或利用现有的驱动程序来禁用引擎的功能,就像我们对待EDR一样(如果您感兴趣,可以查看著名的有漏洞的Capcom驱动程序)。
在继续之前,我要强调我不鼓励将以下技术用于未经授权的黑客行为或在线游戏作弊等恶意目的。这只是一个概念验证,展示了它们在您获得测试权限的环境中可能被滥用的方式。
在浏览一些论坛时,我很快找到了一个可能对我问题集有答案的人。
看着这个证书,它甚至是在我们的截止日期之前,也就是2015年7月29日之前创建的!关于驱动程序证书的另一个有趣事实是,微软通常不关心证书是否过期。只要它曾经是有效的。这种情况可能会在将来发生改变,但目前是允许的。
Microsoft允许使用他们的SignTool和适当的交叉证书对驱动程序进行签名。交叉证书是“由一个证书颁发机构(CA)颁发的数字证书,用于签署另一个证书颁发机构的根证书的公钥。交叉证书提供了一种从单个受信任的根CA到多个其他CA的信任链的方法”。
交叉证书的作用包括:
◆使操作系统内核具有单个受信任的Microsoft根证书颁发机构
◆将信任链扩展到多个商业CA,这些CA颁发用于对Windows上的软件进行分发、安装和加载的软件发布者证书(SPC)
微软的官方文档页面提供了每个CA交叉证书的下载链接。
由于我们的证书是由“VeriSign Class 3 Public Primary Certification Authority”颁发的,我们将下载相应的证书。使用证书和交叉证书,我们可以对我们的恶意驱动程序进行签名。
要对证书进行签名,我们将使用之前提到的SignTool工具。
正如我们所看到的,我们遇到了一个小问题。它表示我们没有符合条件的证书。请记住,该证书在2014年11月过期。事实证明,我们可以在系统时间上做一些手脚。
有了一个“有效”的证书,我们现在应该可以顺利加载驱动程序了。
当我们运行对应的evilcli.exe应用程序时,我们现在可以利用我们新驱动程序的功能了。
为了展示应用程序和驱动程序之间的关联性,下面是在未启动驱动程序的情况下运行应用程序时发生的情况。
十
将一切整合在一起
最后,让我们使用我们的恶意程序来使我们的EDR驱动程序中的进程、线程和加载映像回调失效,并执行Mimikatz以获取完整的密码转储。
首先,我们可以看到我们的EDR服务正在运行(你只能相信我的话)。
为了展示恢复EDR回调函数:
11
潜在的检测
一般而言,杀毒软件和其他安全设备通常不会对驱动程序进行严格的审查。它们通常比普通用户应用程序更受信任。因此,病毒签名可能不是检测恶意驱动程序最可靠的方法(在我的文件中,EDR没有发现任何病毒检测)。
此外,许多EDR并没有实施反篡改措施来检查其回调函数是否被清零或更改。原因可能是由于EDR运行在内核中,不希望通过持续检查来增加额外的CPU开销。随着新的研究的出现,这种情况可能会改变,但目前我们也不能依赖EDR来进行检查。
我发现的是,Windows事件日志实际上记录了在系统日志中加载驱动程序的情况。下面是一个正常的EventID 7045(已安装新服务)示例,对应一个合法的Dell驱动程序。这些事件发生在计算机上安装新的服务/驱动程序时。当您安装打印机、无线网络或其他驱动程序时,可能会看到这些事件。
除了一个边缘情况(至少在我的机器上),当加载内核模式驱动程序时,安全标识符(SID)始终为“S-1-5-18”(本地系统帐户)。
正如您所看到的,在加载我们的恶意驱动程序时,它是由用户SID安装的:
可以理解,在实际的攻击活动中,驱动程序可能会有一个合法的名称,并安装在System32\Drivers\目录中。
我相信,如果您使用SYSTEM权限创建服务,情况可能会有所不同,但通常这需要像PSEXEC或漏洞利用这样的工具,这样很可能会产生更多的噪音,并有更大的可能性被杀毒软件/EDR检测到。
这种检测可能并不完美,因为存在一些边缘情况。下面是安装Npcap Packet Driver的示例,它是Wireshark安装的一部分。但我想象在非技术性的商业环境中,这样的驱动程序可能不会在普通工作站上安装。
看雪ID:Max_hhg
https://bbs.kanxue.com/user-home-876202.htm
# 往期推荐
球分享
球点赞
球在看