—由__Iglenson Security 的__Jordi Iglesias (d4skor)于 2024 年 6 月 11 日发布。
在本文中,我们将展示“规避型”恶意软件是如何制作的,以及它如何成功绕过主要 EDR 供应商的检测机制。目标是帮助非安全人员深入了解这个有时隐蔽的领域,揭开恶意软件实际开发过程的神秘面纱,以及为什么 EDR 本身不足以阻止攻击者。
隐形飞机的“隐身”原则也适用于规避恶意软件。
·关于恶意软件、防病毒和 EDR
∘检测和响应(有时)
∘ EDR 背后
·了解 EDR 及其欺骗方式
∘好的、坏的和丑陋的:来自地狱的 Windows API
∘ API 挂钩的快速介绍
∘设置鼠标陷阱
∘摆脱 EDR 的挂钩:Syscall 传奇
·使用自定义恶意软件绕过主要 EDR
∘恶意软件工件
∘溜走:逃避 EDR 的概念证明
·结论
向公众介绍一下,恶意软件只是具有非常规、恶意(而且相当可疑)目的的普通软件。这意味着它的开发方式与其他程序一样,通常使用 C/C++ 或甚至汇编等低级语言。它使用 Windows API、内存管理函数和其他常见内容,因为恶意软件的主要目标是看起来像一个合法程序,因此它通常会被编码以尽可能地融入环境。
现在谈谈看门狗:防病毒软件是最常见的产品,用于检测包含恶意软件的文件和系统中发生的一些恶意行为。最初,AV 的检测是所谓的“基于签名”或静态的。这意味着他们抓取一个可疑文件,并将其内容、元数据或字节模式与供应商已知的恶意软件数据库进行比较。当然,恶意软件开发人员可以通过“混淆”原始代码并重新编译程序等方式轻松绕过这一点,因为这会改变大多数静态签名。
当然,行业在向前发展,现代防病毒解决方案提供了更先进的检测,例如内存扫描(再次搜索已知模式,但在内存而不是磁盘中)、行为或沙盒检测(在安全的沙盒中运行可疑文件以查看其作用)通常与云分析配对,以提供更强大的检查,而不会占用端点的硬件资源。
现在,让我们来谈谈经常被误解的行业巨头:EDR(端点检测和响应)。它们通常被视为某种神奇的黑匣子,可以阻止所有威胁。但如果我必须快速总结一下,我会说 EDR是一种安全产品,它从安装它的端点收集大量“遥测”数据,例如进程/用户/网络/服务活动等,并从多个不同的来源收集这些数据。
EDR 旨在与防病毒软件协同工作,并管理其警报和检测。有时 EDR 解决方案附带 AV 引擎,而其他解决方案则使用 Microsoft Defender 的默认引擎。无论如何,EDR 永远不应取代 AV,反之亦然。
快速浏览下表可以帮助您了解不同商业 EDR 拥有的大量数据源。
来源:@tsale 的 EDR-Telemetry 项目
几乎所有正常用户、恶意软件或攻击者所做的事都会从这些来源生成遥测数据。这些数据和信息本身由 EDR 的“算法”解释,并根据其编程和配置方式发出警报或采取某些行动。
即使 EDR 错过了威胁并被成功“绕过”,如果经过蓝队的适当分析,EDR 根据恶意行为者的行为持续生成的所有遥测数据很可能会暴露他们。
因此,如果我们忽略针对普通 IT 团队的 EDR 推销,所有这些海量遥测数据的真正价值在于,它是安全运营中心 (SOC) 分析师、威胁猎人和其他“蓝队”防御专业人员的一条重要馈送管道,他们的任务是处理这些数据并确定某些活动是否是恶意的。从本质上讲,EDR 流程可以被视为一种“传感器”,可实时向防御团队提供端点和用户信息。
不同 EDR 之间的关键区别不在于它们在某些测试中自己检测到了多少恶意软件(这也很重要),而是它们为专业人员提供的可视性和信息,以便进一步分析和检测(并做出响应)。如果 EDR 是盲目的,那么蓝队在很大程度上也是如此。所以你最好选择一个有很多眼睛可以看的 EDR。
现在,本文的重点是EDR 本身是不够的。EDR旨在作为一种工具,为安全分析师提供可视性。如果没有人管理它们,它们生成的大部分遥测数据都会被忽略, EDR 也会按照自己的标准自行处理。默认情况下,它们不会阻止和检测一些现代和复杂的攻击,因为它们实际上很难识别,但它们几乎肯定会记录并提供蓝队追捕攻击者所需的一切。如果 EDR 应该自主阻止各种攻击,就像销售人员经常告诉系统管理员的那样,那么人们可以预料到它们也会发出难以承受的大量误报警报。
因此,最好的情况是,公司部署一些优秀的 EDR,然后配备 24/7 安全运营中心、威胁搜寻团队(可实时调查任何可疑情况)和其他安全专家。但显然,拥有一支内部“蓝队”对于绝大多数中小型公司来说实在是负担不起。
这就是我们看到市场上 EDR 相关附加服务不断增加的主要原因,主要针对中小企业。在少数几种服务中,托管 EDR 服务可能是所有服务中最好的。得益于规模经济和外包,它们通常为每个端点提供一个 EDR“传感器”,以及对所有生成的遥测数据进行 24/7 SOC 监控。不仅如此,还包括实时威胁搜寻团队服务,该服务将深入调查任何可疑事件,以检测和搜寻绕过第一道防线(EDR 和防病毒软件)的攻击者。所有这些都在极具竞争力的价格范围内。可以说这是资本主义的奇迹 :)
在介绍恶意软件之前,我们必须首先解释一下恶意软件通常会做什么,以及 EDR 如何在较低级别检测它们。
为此,还将介绍 Windows API Hooking(一种常见的 EDR 检测技术)以及如何规避它以帮助恶意软件不被发现。
EDR 和其他检查可疑软件行为的安全解决方案通常会尝试找到可被视为妥协指标(IOC) 的连续操作的已知模式,因为它们几乎只用于恶意活动。
任何“进程注入”技术的想法都是让_目标_进程执行所谓的“有效负载”,这通常是执行后使受害计算机重新连接到攻击者的实际代码。此有效负载通常位于恶意软件中,可以是任何内容,从 cmd 命令到反向 shell,当然还有连接到攻击者控制的 C2 服务器(命令和控制)的_代理_。目标进程可以是恶意软件自己的进程,也可以是远程进程,以实现更隐蔽性,例如 notepad.exe 就是一个通用示例。
一个臭名昭著的方法是经典的进程注入技术。它包括使用 3 个 Windows API:
VirtualAllocEx:在远程进程上分配虚拟内存,有效负载将被复制到其中。
VirtualProtectEx:将远程进程分配的内存(现在包含我们的有效负载)的内存权限设置为 RWX(读取、写入、执行),以便下一步能够正确执行。
CreateRemoteThread:在远程进程上启动一个新线程,该线程将运行分配的 Payload 代码,因为该内存空间现在可以执行。
因此,请记住,恶意软件开发人员的目标是使程序看起来尽可能合法,因此显然应该避免这种非常常见且易于检测的_进程注入_技术。我们将在本文后面展示我们制作的自定义恶意软件时,使用不同的进程注入程序作为示例。
如果您有机会阅读一些有关进程注入技术的文章,就会发现现有的执行有效负载的原始方法数量惊人。恶意软件开发的相对美妙之处在于,它具有足够的创造力来欺骗 EDR 甚至蓝队专家,方法是对恶意软件进行编程以执行绝对出乎意料的事情,试图以与任何其他已知恶意软件都不相关的方式进行这些操作,并有效地伪装成良性软件。
EDR 确定恶意行为的最著名技术之一是所谓的 Windows API“挂钩”。
在 EDR 用来捕获恶意软件的众多技术中,我们将特别概述这一技术,因为它是最受欢迎的技术之一。由于有无数文章深入讨论了这一点,因此这里的目的不是让您无聊地解释 Windows Internals 或任何不同的 API Hooking 规避技术,而是帮助您了解恶意软件开发人员如何每次都以新的方式规避每个新检测功能。希望通过这个 API Hooking 规避示例,您可以了解为什么这通常被称为“猫捉老鼠游戏”。
请注意,由于检测机制的快速发展,现代 EDR 中 API Hooking 的使用逐年减少。因此,它很快就会成为一种过时的技术,尽管它可以作为本文的示例。
API 挂钩是一种通用技术,用于拦截和修改 API 函数(例如,前面提到的三个函数之一)的行为。这通常用于调试或逆向工程。它涉及用自定义版本替换 API 函数的原始实现,该版本在调用原始函数之前或之后执行一些额外操作。这允许人们在不修改其源代码的情况下修改程序的行为,而只需修改导入的 API。
Windows API 挂钩是 AV/EDR 解决方案用于确定代码是否恶意的技术之一。如果它挂钩常见的进程注入 API,挂钩可以深入了解可执行文件试图执行的操作。
实现 API 挂钩的经典方法是通过_trampolines_实现的。trampoline 是一种“跳转”,用于通过跳转到进程内存中的另一个地址来改变代码执行路径。
蹦床的跳转插入到函数的开始处,导致该函数被钩住。
当调用挂钩函数时,将触发蹦床跳跃。
然后,执行流被传递并改变到另一个地址,从而导致执行不同的函数。
如果这看起来有点令人困惑,请不要担心,接下来它会更有意义。
让我们看看 EDR 如何实现 API Hooking,以检测关键 Windows API 中的恶意行为。
Windows API 是 NT 或 Native API 的抽象,用于简化普通程序员的工作。这些 API 通常以“Nt”开头,以便于区分,并且从名为 NTDLL 的 Windows DLL 库中导出。
每当我们调用 Windows API 时,实际上执行的是 Native API。EDR 实际上不会挂接 Windows API,而是挂接它的 Native 版本。以下是 Windows API 与其各自的 Native API 的一些示例:
虚拟内存分配Ex->NtAllocateVirtualMemory
虚拟保护Ex->NtProtect虚拟内存
创建远程线程->NtCreateThreadEx
以下几张图可以帮助您更好地直观地了解 EDR 的挂钩过程。第一张图显示了未挂钩的 VirtualProtectEx,第二张图显示了 EDR 如何挂钩其底层 Native 函数 NtProtectVirtualMemory。
未挂钩的 VirtualProtectEx 函数
挂钩 VirtualProtectEx 函数
现在我们来看一个真实的例子,看看我们的目标 EDR 如何尝试挂钩三个臭名昭著的 Windows API 之一(正如我们刚才提到的,它挂钩的是它们的 Native 版本)。我们将分析的 API 是 VirtualProtectEx,它在下面执行 Native NtProtectVirtualMemory 函数。运行的程序将是一个简单的C 语言_Hello World!_
为了更清楚地了解这是如何实现的,我们将查看来自调试器的几个屏幕截图,这些屏幕截图显示了调用 API 时最终将执行的汇编指令。您不需要了解汇编指令本身,只需知道当执行“syscall”时,执行将转到内核,并由 CPU 实际处理。因此,如果某些东西在“ mov r10, rcx ”和“ _syscall”_之间中断执行, API 将永远不会运行。
这是Native API 的原始状态,没有安装 EDR:如您所见,mov r10,rcx .... syscall。
但是,在第二张截图中,由于已经安装了 EDR,我们可以看到第一条指令已被修改,现在包含一条 JUMP 指令。这就是我们提到的实际 HOOK或 Trampoline。
那么,您可能想知道,这个跳转会去哪里?它将执行流重定向到……EDR!它是如何做到的?EDR 通常有一个 DLL(库)组件,该组件在创建时会注入到系统中的每个进程中。是的,即使在这个简单的 Hello World 程序中也是如此。一旦注入,EDR 就会在每个关键或敏感的 Native API 函数(如上述 3 个)上安装一个“钩子”,然后该钩子会跳转到 EDR 的 DLL。
因此,每次调用其中一个挂钩函数时,EDR 的 DLL 都会在运行“syscall”之前获得执行权,并且由于它具有内部逻辑来确定某个函数是否被滥用, 如果没有发现威胁,它将返回到原始执行并运行“syscall”,否则它将停止执行并发出警报。
调试器显示 Hello World 进程中已加载的“模块”。我们可以看到 kernelbase.dll、ntdll.dll 和其他预期的 DLL。但红色的 DLL 属于 EDR。DLL 名称已模糊处理,以避免与供应商产生法律问题。
到这一步,游戏结束了,对吧?我们不应该在 EDR 不知情的情况下执行任何有用的 API 函数,对吧?……继续阅读 :)
在开始实施挂钩之前,如果您仍然无法理解其中的原理,请想象一下房屋的安全系统。EDR 是控制面板,每扇门窗上都有一个传感器,每当门窗打开时都会通知控制面板。此传感器是敏感 Windows API 中的挂钩。每次打开门(触发挂钩)时,控制面板 (EDR) 都会检查是否有异常情况发生,或者只是孩子们去后院(常规使用)。因此,如果凌晨 3 点后院窗户打开(调用敏感的挂钩 API),并且花园的运动传感器也被触发(使用了另一个敏感的挂钩 API),那么这肯定是一个“妥协指标”。
自从 EDR 凭借其出色的钩子技术实现了捕获恶意软件的美好愿望以来,恶意软件开发人员对其进行了逆向工程,显然找到了 101 种规避钩子的方法。最流行的规避钩子的方法是直接和间接系统调用。
注意:“syscalls”除了是汇编指令外,还用于指代 Native API 本身,如 NtProtectVirtualMemory。因此 Native API 和 syscall 可以互换使用。
即使我们手动调用与已挂钩的 Windows API(如 VirtualProtect)相对应的 Native API,挂钩仍会抓住我们。因此,大多数 EDR 挂钩规避技术的起点是手动实现所需“系统调用”(Native API 函数)的汇编代码并直接调用它们。
回顾一下原始的、未挂钩的 Syscall/Native API 在汇编中的样子
标准的钩子触发过程是调用 Windows API,然后调用放置钩子的 Native API。在这里,执行跳转到 EDR 的 DLL,以确定该行为是否是恶意的。如果不是,则执行将返回到 Native API 的汇编代码,最后执行_syscall指令:_
这将是常规的 Windows API 使用,最终会按预期落入 EDR 的钩子。
为了避免使用钩子,我们将直接执行系统调用,而不是采用这种典型的程序,这意味着如果不存在钩子,则手动调用目标函数最终将运行的相同汇编指令,而不调用其上的 API 和钩子。这就是“直接系统调用”这个名称的由来。
因此,当“直接”调用_系统调用_或本机 API时,会发生以下情况,这样我们就完全避免了 EDR 的钩子:
通过这个简单的操作,EDR的钩子就被有效绕过了。
现在我们终于可以进行恶意软件概念验证了。
不过,如果逃避 EDR 仅包括这一点,那么恶意软件开发人员的生活就太容易了。请注意,我们跳过了获取“SSN”的部分,它类似于本机 API/系统调用 ID,而获取此 SSN 是一项复杂的任务,为此已经开发了几种不同的技术。
无论如何,直接系统调用现在已经过时了,我们刚刚解释的过程开始被检测到,所以后来出现了间接系统调用和大量用于执行这些操作的技术……有一天,EDR 开始监控调用堆栈,所以必须执行调用堆栈欺骗,直到 EDR 学会从内核本身收集遥测数据,因此我们在“用户空间”所做的任何事情都变得毫无用处。希望您已经了解了猫和老鼠的工作原理。
为了证明本文到目前为止提出的观点和陈述,本节专门介绍如何绕过众所周知的 EDR。因此,对于那些仍然相信这些产品 100% 可靠(如销售人员所说)的人,请继续阅读。(产品和供应商名称将被模糊处理以避免法律问题)。
这应该可以作为对 EDR 供应商咄咄逼人和误导性销售声明的反宣传。尽管它们仍然很有价值和必要,但我希望现在你已经知道,如果没有人查看它们的遥测数据,EDR 本身是不够的,因为它们的独立检测可以被绕过,尽管它们会继续从恶意软件所做的任何事情中生成有价值的数据。
我们将展示定制的、相对简单的木马恶意软件如何逃避检测并在受 EDR 保护的计算机上建立远程访问后门。
基本上,在设计这种恶意软件时,我们试图避免诸如“好的,坏的,丑陋的”等常见模式。
首先,关于有效载荷本身,我们将使用_Havoc C2_(命令和控制)代理。这是代码,执行后,将使受害者计算机连接到我们的“攻击者”C2 服务器,在受害者上建立交互式后门并授予我们远程访问权限。此有效载荷将在恶意软件代码内加密,并且仅在运行时解密,因此我们可以避免“静态”或签名检测。此步骤很重要,因为由于这个 C2 项目是开源且众所周知的,因此防病毒软件在分析文件时拥有大量“签名”来检测其生成的有效载荷。
进程注入(将有效载荷移入远程进程的内存)将利用一种称为“映射注入”的技术,该技术包括将有效载荷移入“映射”内存区域,以避免使用 VirtualAllocEx 时创建的典型“私有”内存部分。EDR 对具有可执行权限的私有内存区域非常怀疑,而这正是我们使用 VirtualAllocEx 时最终会得到的。
对于有效载荷的执行,主要思想是尽量避免使用 CreateRemoteThread,因为我们已经知道它是“丑陋的”API。在这个例子中,我们将使用一种滥用 Windows 功能的新技术,称为 ThreadPools。使用线程池,我们根本不需要创建线程,相反,我们可以让池处理有效载荷的执行,大大增加了未被发现的机会。
关于API Hook 规避,我们使用了一种名为“Hell's Hall”的间接系统调用技术,它是 Hell's Gate、Halo's Gate 和 Tartarus Gate 的演变(是的,规避技术的名称就是这么奇怪)。这些都是我们已经解释过的_直接系统调用_基本概念的迭代,但在某些方面有所改进。
当然,这款恶意软件还有更多功能,例如反分析功能或反虚拟环境功能,如果恶意软件在 EDR 或防病毒沙盒中进行扫描,这些功能可阻止恶意软件运行。但目前这不是主题。
对于此恶意软件演示,出于隐蔽原因,我们的恶意程序注入并运行有效负载的远程进程将是 Microsoft Edge。这是因为,如果我们注入 Notepad.exe,如果它开始连接到我们的攻击者 C2 服务器的互联网,我想每个人都会怀疑 :) 但 Edge 一直在生成互联网流量,所以这并不奇怪。
快速回顾一下我们在本文中解释的所有内容,主要内容如下:
EDR 确实是很好的检测工具,但人们不应该真的只信任它们,而应该尝试让专门的安全团队为其提供支持,无论是托管 EDR 服务还是成熟的蓝队。一般来说,这不应该是 IT 团队或系统管理员的任务。
恶意软件开发不是巫术,它只是制作具有不良意图的常规软件,并且通常被编码为看起来尽可能无害。
攻击者和恶意软件开发者总是比蓝队(防御者)有优势,因为人类在编写恶意软件时的原始想法本质上是不可预测的。这也解释了为什么 EDR 识别的模式很容易被简单的创造性和出其不意所欺骗。
不存在 EDR 或任何安全解决方案能够 100% 检测出威胁。这种东西永远不会存在。除了现有的安全产品外,所有公司都必须确保对其内部和外部基础设施进行适当的强化和渗透测试,以降低如果绕过第一道恶意软件防御线(EDR 和防病毒软件),攻击会变得至关重要的可能性。