公司动态

拉撒路与 FudModule 根套件:超越 BYOVD 的管理员到内核零日漏洞

2024-12-26 15:22:45



重点

Avast 发现了一种在真实环境中利用 appidsys AppLocker 驱动程序的管理员到内核漏洞的零日漏洞利用。 感谢 Avast 的及时报告,微软在 2024年2月的补丁星期二更新中修复了此漏洞,编号为 CVE202421338。 该漏洞的利用活动是由臭名昭著的 Lazarus 组策划,最终目标是建立内核读/写原语。 这一原语使得 Lazarus 能够在其数据型 FudModule 根套件的更新版本中进行直接的内核对象操作,之前版本曾由 ESET 和 AhnLab 进行过分析。 Avast 完全逆向工程了这一更新的根套件变体,识别出在功能性和隐蔽性方面的显著进步,包括四项新技术和三项更新的根套件技术。 主要进展是根套件现在采用了新的句柄表条目操作技术,试图暂挂与微软 Defender、CrowdStrike Falcon 和 HitmanPro 相关的受保护进程。 另一个显著的提升是利用这个零日漏洞,相较于过去使用明显的 BYOVDBring Your Own Vulnerable Driver技术越过管理员到内核的界限。 Avast 的调查还回溯了大型感染链,探测到了一个新 RAT远程访问木马,归因于 Lazarus。 有关 RAT 的技术细节和初始感染向量将在随我们 Black Hat Asia 2024 预报 一起发布的后续博客中公开。

引言

在 Windows 安全领域,管理员与内核之间存在一条微妙的界限。微软的 安全服务标准 一直以来都主张“管理员到内核不算作安全边界”,这意味着微软保留在自己认为合适的情况下修补管理员到内核漏洞的权利。因此,Windows 安全模型并不保证能够防止管理员级别的攻击者直接访问内核。这并不仅仅是一个理论问题,实际上,拥有管理员权限的攻击者经常通过利用已知易受攻击的驱动程序实现内核级访问,这种技术称为 BYOVDBring Your Own Vulnerable Driver。

不过,微软并没有放弃保障管理员到内核的边界。相反,他们在提高这一边界的安保方面取得了很大进展。深度防御保护机制,例如 DSE驱动签名强制执行或 HVCI虚拟化保护代码完整性,使攻击者在内核中执行自定义代码变得越来越难,迫使大多数攻击者转而进行仅以数据为目的的攻击通过读取和写入内核内存实现恶意目标。其他防御机制,比如 驱动程序阻止列表,也在推动攻击者转向利用较少被知道的漏洞驱动程序,从而导致攻击复杂性的增加。尽管这些防御机制尚未达到能够正式称之为“管理员到内核”的安全边界BYOVD 攻击依然可行,因此称之为安全边界无疑会误导用户产生虚假的安全感,但它们显然代表了朝正确方向的步伐。

从攻击者的角度来看,从管理员到内核的跨越开启了新的可能性。通过内核级访问,攻击者可能会干扰安全软件、隐藏感染迹象包括文件、网络活动、进程等、禁用内核模式的遥测、关闭缓解措施等等。此外,由于 PPL受保护进程轻型安全性依赖于管理员到内核的边界,我们假设的攻击者还获得了篡改受保护进程或为任意进程添加保护的能力。如果 lsass 被 RunAsPPL 保护,绕过 PPL 可能会使攻击者能够转储不可达的凭据。

有关攻击者可能想通过内核级访问实现的更具体示例,请继续阅读这篇博客在后半部分,我们将深入探讨 FudModule 根套件中实现的所有技术。

依靠土地生存:易受攻击的驱动程序版块

随着越来越多的攻击者试图滥用上述内核功能,防御者别无选择,只能积极追寻驱动程序漏洞。因此,想要针对防御良好的网络的攻击者也必需提升他们的技术,以便避免被检测到。我们可以大致将管理员到内核的驱动程序漏洞分为三类,每一类都反映了攻击难度和隐蔽性之间的权衡。

NDay BYOVD 漏洞利用

在最简单的情况下,攻击者可以利用 BYOVD 来利用公众已知的 nday 漏洞。这非常容易实现,因为有很多针对各种漏洞的公共概念证明PoC 漏洞利用。然而,这也相对容易被检测,因为攻击者必须首先将一个已知易受攻击的驱动程序放到文件系统中,然后将其加载到内核中,从而造成两个很好的检测机会。此外,某些系统可能启用了微软的 易受攻击驱动程序阻止列表,这将阻止一些最常见的易受攻击的驱动程序加载。之前的 [FudModule 根套件的版本可归入此类别,起初利用了 dbutil23sys 中的已知漏洞,随后又在后来版本中转向针对 enesys。

零日 BYOVD 漏洞利用

在更复杂的情况下,攻击者将利用 BYOVD 来挖掘已签名第三方驱动程序中的零日漏洞。自然,这要求攻击者首先发现这样的零日漏洞,这在最初看起来可能是个令人望而生畏的任务。然而,值得注意的是,任何可利用的漏洞在任何签名驱动程序中都可以,而不幸的是,低质量第三方驱动程序层出不穷。因此,发现这样的漏洞的难度可能并不像最初想象的那样高。扫描一组驱动程序以寻找已知的漏洞模式似乎就足够了,正如 Carbon Black 研究人员最近通过大规模静态分析发现了 200 多个签名驱动程序中的 34 个独特漏洞那样。这种零日 BYOVD 攻击明显比 nday 攻击更隐蔽,因为防御者无法依赖已知易受攻击驱动程序的哈希进行检测。然而,仍然存在某些检测机会,因为加载随机驱动程序表示一个可疑事件,可能需要更深入的调查。以间谍软件供应商 Candiru 的攻击为例,我们曾捕获 它利用了 hwsys 中的一个零日漏洞,作为其浏览器攻击链的最终提权阶段。

超越 BYOVD

最后,管理员到内核的“圣杯”是通过利用已在目标计算机上安装的驱动程序中的零日漏洞来超越 BYOVD。为了使攻击尽可能普遍,最明显的目标将是已经作为操作系统一部分的内置 Windows 驱动程序。

在这样的驱动程序中发现可利用的漏洞比之前的 BYOVD 情景难得多,原因有两个。首先,可能的目标驱动程序数量小得多,从而导致攻击面大幅缩小。其次,内置驱动程序的代码质量无论如何都要比随机第三方驱动程序高得多,这使得发现漏洞更加困难。值得注意的是尽管修补通常无法有效阻止 BYOVD 攻击即使厂商修补了其驱动程序,攻击者仍然可以滥用老旧的、未修补的驱动版本但修补内置驱动程序的确会使这种零日攻击不再可用。

如果攻击者尽管面临所有这些障碍,成功利用了内置驱动程序中的零日漏洞,他们将获得一种无法与标准 BYOVD 利用相提并论的隐蔽性。通过利用这种漏洞,攻击者可以说是依靠土地生存,无需带入、投放或加载任何自定义驱动程序,从而使内核攻击得以真正无文件执行。这不仅逃避了大多数检测机制,还使攻击可以在施行驱动程序允许提交的系统上进行这似乎有点讽刺,因为 CVE202421338 关注的是 AppLocker 驱动程序。

尽管我们只能推测 Lazarus 选择这种第三种跨越管理员到内核边界方法的动机,但我们认为隐蔽性是他们主要的动机。鉴于他们的声名显赫,不得不每当有人曝露他们当前使用的 BYOVD 技术时就要更换漏洞。也许他们还考虑到,超越 BYOVD,能够保持不被检测,从而减少更换漏洞的需要。

CVE202421338

就零日漏洞而言,CVE202421338 相对容易理解和利用。该漏洞存在于 appidsys 的 IOCTL输入和输出控制调度器中,这是 Windows 内置应用程序白名单技术 AppLocker 的核心驱动程序。易受攻击的控制代码 0x22A018 旨在计算可执行图像文件的 智能哈希。该 IOCTL 通过允许调用者指定驱动程序应如何查询和读取被哈希文件,提供了一些灵活性。问题在于,这种灵活性是通过期望从 IOCTL 的输入缓冲区中引用两个内核函数指针来实现的:一个包含回调指针以查询哈希文件的大小,另一个则是读取要被哈希的数据的回调指针。

猎豹加速器官网

由于用户模式通常不处理内核函数指针,因此这种设计表明 IOCTL 可能最初是为了从内核中调用的。确实,我们没有找到任何合法的用户模式调用者,但这个 IOCTL 确实被其他 AppLocker 驱动程序调用。例如,在 applockerfltrsys 中有一个 ZwDeviceIoControlFile 调用,传递 SmpQueryFile 和 SmpReadFile 作为回调指针。此外,appidsys 自身也使用这个功能,传递 AipQueryFileHandle 和 AipReadFileHandle基本上就是对 ZwQueryInformationFile 和 ZwReadFile 的封装。

尽管有这种设计,易受攻击的 IOCTL 仍然可以从用户空间访问,这意味着用户空间攻击者可以利用它来基本上欺骗内核调用任意指针。而且,攻击者也部分控制了传递给调用的回调函数的第一个参数引用的数据。这提供了理想的利用场景,允许攻击者以上高控制权调用任意内核函数。

触发漏洞的 WinDbg 会话,追踪到任意回调调用。注意攻击者控制的被调用函数指针此会话中为 0xdeadbeefdeadbeef和第一个参数所指向的数据0xbaadf00dbaadf00d。

如果看起来漏洞利用显得容易,注意这一漏洞允许攻击者调用的指针有一些约束。当然,如果存在 SMEP管理员模式执行防护,攻击者无法简单地提供用户模式的 shellcode 指针。此外,回调调用是间接调用,可能受到 kCFG内核控制流保护的保护,需要提供的内核指针代表有效的 kCFG 调用目标。实际上,这并不妨碍利用,因为攻击者可以只是找到一些符合 kCFG 要求的 gadget 函数,将其转化为另一个原语,比如有限制的读/写。为了达到易受攻击的回调调用,还需要满足输入缓冲区中的其他一些约束。然而,这些限制也相对容易满足,因为攻击者只需要伪造一些内核对象并提供正确的值,以便 IOCTL 处理程序通过所有必要检查,同时不导致内核崩溃。

易受攻击的 IOCTL 通过名为 DeviceAppId 的设备对象进行暴露。分解 0x22A018 控制码并提取 RequiredAccess 字段显示,调用它需要一个具有写访问的句柄。检查设备的 ACL访问控制列表;见下图,其中有 local service、administrators 和 appidsvc 的条目。尽管 administrators 的条目没有授予写入访问,但 local service 的条目确实如此。因此,更准确地描述 CVE202421338,我们应标记其为 local servicetokernel 而不是 admintokernel。另外值得注意的是,appidsys 可能会创建两个额外的设备对象,即 DeviceAppidEDPPlugin 和 DeviceSrpDevice。虽然这些权限更宽松,但通过它们无法访问易受攻击的 IOCTL 处理程序,将其排除在利用目的之外。

DeviceAppId 的访问控制条目,表明 local service 被允许写入访问,但 administrators 并未。

由于 local service 账户相对于管理员具有较低的权限,这也使得该漏洞比标准的管理员到内核漏洞具有更高的影响。这可能是微软将此 CVE 描述为 Privileges Required Low 的原因,因为 local service 进程并不一定要以更高的完整性级别运行。然而,出于本文的目的,我们仍然选择将 CVE202421338 主要称为管理员到内核漏洞,因为我们认为这更好地反映了它在真实环境中的使用情况Lazarus 已经以提升的权限运行,并在调用 IOCTL 之前伪装成了本地服务账户。

该漏洞在 Win10 1703RS2/15063中引入,首次实现了 0x22A018 IOCTL 处理程序。旧版本未受影响,因为它们不支持该易受攻击的 IOCTL。有趣的是,Lazarus 攻击在遇到早于 Win10 1809RS5/17763的构建时就会中止,完全无视三个理应处于易受攻击状态的 Windows 版本。至于后来的版本,该漏洞一直延续到最新构建,包括 Win11 23H2。虽然 IOCTL 进行了些许更改,包括输入缓冲区中期望的额外参数,但没有阻止利用的措施。

我们开发了一个定制的 PoC概念证明漏洞利用,并于 2023 年 8 月以漏洞报告的方式提交给微软,促成了在 2 月补丁星期二更新中发布的 CVE202421338 的建议。该更新通过向 IOCTL 处理程序添加 ExGetPreviousMode 检查来解决漏洞见下方的补丁。这旨在防止由用户模式发起的 IOCTL 导致任意回调的触发。

修补过的 IOCTL 处理程序。如果启用功能 2959575357,尝试以 PreviousMode==UserMode 调用 IOCTL 应立即导致 STATUSINVALIDDEVICEREQUEST,甚至未能达到 AipSmartHashImageFile。

尽管这一漏洞的修补可能仅勉强符合微软的安全服务标准,但我们认为修补是正确的选择,并且想要感谢微软最终解决了这个问题。修补无疑将扰乱 Lazarus 的进攻性操作,迫使他们找到新的管理员到内核的零日,或重新采用 BYOVD 技术。虽然发现管理员到内核的零日可能并不像发现具有更吸引人的攻击面例如标准用户到内核,甚至沙箱到内核中的零日那样具有挑战性,但我们认为发现一个仍将需要 Lazarus 投资大量资源,并可能分散他们攻击其他不幸目标的注意力。

利用

Lazarus 的利用始于初始化阶段,为利用和根套件执行一次性设置两者已编译为同一模块。此初始化过程首先动态解析所有必需的 Windows API 函数,然后对 PEBBeingDebugged 进行简单的反调试检查。随后,利用会检查构建号,以确定它是否在支持的 Windows 版本上运行。如果是,它会加载适合当前构建的硬编码常量。有趣的是,常量选择有时取决于更新构建修订号UBR,显示出在确保代码在各类目标机器上正常运行的方面的高度投入。

反编译代码片段,加载与版本相关的硬编码常量。此特定示例包含 Win10 1809 的偏移量和系统调用编号。

初始化过程继续泄漏三个内核模块的基本地址:ntoskrnl、netio 和 fltmgr。通过调用 NtQuerySystemInformation 使用 SystemModuleInformation 类来实现。当前执行线程的 KTHREAD 地址也通过类似的方式泄漏,通过复制当前线程伪句柄然后使用 SystemExtendedHandleInformation 系统信息类找到相应的内核对象地址。最后,利用手动将 ntoskrnl 映像加载到用户地址空间中,只为扫描一些感兴趣函数的相对虚拟地址RVA。

由于 appidsys 驱动程序不必已经加载在目标机器上,利用可能首先需要自己加载它。它选择通过写入一个特定的与 AppLocker 相关的 ETWWindows 事件跟踪提供程序发布事件的间接方式实现此目标。一旦 appidsys 被加载,利用使用直接系统调用 NtSetInformationThread 伪装为 local service 帐户,其 ThreadImpersonationToken 线程信息类。伪装为 local service 后,它可以获得对 DeviceAppId 的读/写句柄。借助该句柄,利用最终准备 IOCTL 输入缓冲区并使用 NtDeviceIoControlFile 系统调用触发漏洞。

在整个利用过程中大量使用直接系统调用。

利用以这样一种方式构造 IOCTL 输入缓冲区,使得易受攻击的回调基本上成为用于将数据从 IOCTL 输入缓冲区复制到任意目标地址的 gadget。这一地址旨在破坏当前线程的 PreviousMode。通过确保 IOCTL 输入缓冲区中的相应源字节为零,复制将清除 PreviousMode 字段,从而使其值被解释为 KernelMode。针对 PreviousMode 的这一技术是广泛流行的利用技术,因为清除 KTHREAD 结构中的这一字节绕过了内核模式检查,允许用户模式攻击者读取和写入任意的内核内存。值得注意的是,尽管这个技术在某些 Windows Insider 版本中已被减轻,但在撰写本文时,这一减轻措施尚未在一般可用版本中发布。

有趣的是,利用可能尝试触发易受攻击的 IOCTL 两次。这是因为在 Win11 22H2 中增加了额外的参数。因此,在较新版本中,IOCTL 处理程序期望输入缓冲区的大小为 0x20 字节,而之前仅期望 0x18 的大小。利用选择不为当前构建使用适当的输入缓冲区大小,而是尝试第一次用 0x18 的输入缓冲区大小调用 IOCTL,之后如果未成功,则使用 0x20 再次尝试调用。这是一种有效的做法,因为 IOCTL 处理程序的第一个动作是检查输入缓冲区大小,如果不匹配所期望的大小,便会立即返回 STATUSINVALIDPARAMETER。

为了检查是否成功,利用采用 NtWriteVirtualMemory 系统调用,尝试读取当前线程的 PreviousModeLazarus 避免使用 NtReadVirtualMemory,稍后会讨论这个情况。如果利用成功,系统调用应返回 STATUSSUCCESS,并且泄漏的 PreviousMode 字节应等于 0意味着 KernelMode。否则,系统调用应返回错误状态代码,因为在未污染 PreviousMode 的情况下读取内核内存是不可能的。

在我们的利用分析中,故意选择省略了一些关键细节,例如选择回调 gadget 函数。这一决定是为了在帮助防御者检查检测时取得相应的平衡,但又不让利用过于广泛可获得。对于那些需要更多防御用途信息的人,我们可以在个别情况下分享更多细节。

FudModule 根套件

管理员到内核利用的首要目标是破坏当前线程的 PreviousMode。这使得一个强大的内核读/写原语成为可能,受影响的用户模式线程能够使用 Nt(ReadWrite)VirtualMemory 系统调用读取和写入任意的内核内存。掌握了这一原语,FudModule 根套件利用直接内核对象操控DKOM技术来破坏各种内核安全机制。值得重申的是,FudModule 是一款数据型根套件,这意味着它完全从用户空间执行,所有内核篡改都是通过读/写原语实现的。

FudModule 根套件的首个变体是由 AhnLab 和 ESET 研究团队独立发现的,二者都在 2022 年 9 月发布了详细分析。根套件因其导出表中使用的字符串 FudModuledll 而命名。该工件虽不再存在,但我们发现的无疑是更新版相同根套件的变体。AhnLab 的报告 记录了来自 2022 年初的样本,其中包含七项数据型根套件技术,通过 BYOVD 漏洞启用 enesys。ESET 的报告 则研究了一个稍早的变体,也具有七项根套件技术,但利用了不同的 BYOVD 漏洞,该漏洞在 dbutil23sys 中。相比之下,我们的发现涉及到一个包含九项根套件技术的样本,利用了一个之前未知的管理员到内核漏洞。在这九项技术中,四项是新的,三项有所改进,两项则与之前的变体保持不变。这使得原七种技术中的两项已被弃用,且在最新变体中不再存在。

每个根套件技术分配了一位从 0x1 到 0x200当前变体保留的 0x20 位未使用。FudModule 按照分配的位升序顺序依次执行这些技术。位用于报告各个技术的成功。在执行过程中,FudModule 将构建一个命名为 bitfieldtechniques 的整数值如下反编译中,只有对应成功执行的技术的位会被设置。最终,这个整数会写入名为 tem1245tmp 的文件,报告根套件的成功。有趣的是,我们未能在其他任何 Lazarus 样本中找到这个文件名,表明该文件仅通过手动操作进行检查,可能通过 RAT远程访问木马命令进行。这一发现支持我们认为 FudModule 仅与 Lazarus 的其他恶意软件生态系统松散集成,并且 Lazarus 在使用根套件时十分审慎,仅在适当情况下根据需求进行部署。

根套件的“主”函数,执行各个根套件技术。请注意缺失的 0x20 技术。

考虑到大量更新,FudModule 似乎仍在积极开发之中。最新的变体显得更加稳固,避免了一些早期版本中可能存在的问题。由于某些技术以我们以前未遇到的方式针对未记录的内核内部结构,我们认为 Lazarus 必须在进行自己的内核研究。此外,尽管根套件的技术上相当复杂,我们还是识别出一些错误。这可能会限制根套件的预期功能,甚至在适当的条件下导致内核崩溃。我们发现其中一些错误十分有趣,并希望能分享更多细节,但并不想为威胁行为者提供免费错误报告,因此我们暂时保留这些信息,如果错误得到修复,可能在未来分享部分信息。

有趣的是,FudModule 采用 NtWriteVirtualMemory 系统调用同时读取和写入内核内存,消除了调用 NtReadVirtualMemory 的必要性。这利用了在限制为单一虚拟地址空间中,NtReadVirtualMemory 和 NtWriteVirtualMemory 在源 Buffer 和目标 BaseAddress 参数 上基本相反操作的特性。换言之,向内核内存写入可以视为从用户模式 Buffer 向内核模式 BaseAddress 进行写入,而从内核内存读取则可以通过交换参数实现,即是从内核模式 Buffer 向用户模式 BaseAddress 写入。Lazarus 的实现充分利用了这一点,似乎是一个故意的设计决定,因为大多数开发者可能倾向于使用 NtReadVirtualMemory 读取内核内存,而使用 NtWriteVirtualMemory 写入内核内存。我们只能猜测 Lazarus 选择这种方法的原因,但这可能是另一个增强隐蔽性的特征。有了他们的实现,攻击者只需使用一个可疑的系统调用,而不是两个,从而可能减少检测机会。

调试信息

在深入分析实际根套件技术之前,还有一件事情值得讨论。令我们惊讶的是,Lazarus 在编译的代码中留下了一些明文调试信息。这类调试信息通常是恶意软件研究人员最希望看到的事情之一,因为它们往往能显著加速逆向工程过程。在这种情况下,然而,其中一些调试信息的作用正好相反,有时甚至让我们质疑自己是否正确理解了代码。

例如,让我们提到字符串 get rop function addresses failed。假设 rop 代表 返回导向编程,那么这个字符串在利用的上下文中非常有意义,尽管事实上并没有整型返回地址被破坏

根套件中找到的明文调试字符串。 安全软件被称为 vaccine。

尽管使用英语编写,但调试字符串表明其作者并非母语人士,有时甚至指出它们可能源于韩国。通过根套件中反复使用的词汇 vaccine,这一点可以很好地显示出来。这起初让我们感到疑惑,因为不清楚疫苗与根套件功能之间的关系。然而,随后很快就清晰了,这个术语被用来指代 安全软件。这可能源于常见的韩语翻译 antivirus ,是一个字面意思为 病毒疫苗 的复合词。值得注意的是,甚至朝鲜“自己的”反病毒软件也被称为 SiliVaccine,而根据我们所知,诸如日语这样的其他语言中不使用这个术语。此外,这并不是讲韩语的威胁行为者第一次使用这个词汇。例如,AhnLab 最近的报告 中提及了以下明显命令:

cmdexe /U /c wmic /namespacerootsecuritycenter2 path antivirusproduct get displayname gt vaccinetxt

另一个谜团是缩写 pvmode,我们认为它指的是 PreviousMode。使用 pvmode 的 Google 查询完全没有相关结果,我们怀疑大多数英语使用者会选择不同的缩写,例如 prvmode 或 prevmode。然而,在与语言专家咨询后,我们得知对于韩国人来说使用 pvmode 也不太常见。

最后,还有调试信息 disableV3Protection passed。根据上下文,相当通用的术语 V3 在这里指的是 AhnLab V3 Endpoint Security。考虑到地缘政治背景,朝鲜黑客组织可能对南韩的 AhnLab 相当熟悉,因此用这种更加不具体的简写称呼其内部组件显得合理。

0x01 注册表回调

第一个根套件技术旨在处理 注册表回调。这是一个文档化的 Windows 机制,允许安全解决方案监控注册表操作。安全解决方案的内核模式组件可以调用 CmRegisterCallbackEx 例程来注册一个回调,以便在系统上执行注册表操作时发送通知。此外,由于回调是同步调用,在实际操作执行之前或之后调用,回调甚至可以阻止或修改被禁止/恶意操作。FudModule 的目标是移除现有的注册表回调,从而干扰依赖于此机制的安全解决方案。

回调的移除本身是通过直接修改内核管理的一些内部数据结构来执行的。这在早期版本中也有类似的做法,正如 ESET 和 AhnLab 的文献所记录的那样。在那里,根套件找到 nt!CallbackListHead 的地址其中包含所有现有注册表回调的双向循环链表,随后简单地将其指向自己。

在当前版本的 FudModule 中,这一技术有所改进,保留了一些选择性的回调,也许使根套件隐蔽性更高。该更新版本的开始与先前情况相同:找到 nt!CallbackListHead 的地址。这是通过解析 CmUnRegisterCallback通过名称执行此解析在内存中的导出表上迭代,扫描其函数体,找到 lea rcx[nt!CallbackListHead] 指令,从指令中提取的偏移量计算出最终地址。

有了 nt!CallbackListHead 地址,FudModule 能够迭代注册表回调链表。它检查每个条目,并确定回调例程是否在 ntoskrnlexe、applockerfltrsys 或 bfssys 中实现。如果是,回调则保持不变。否则,根套件将替换回调例程指针为指向 ObIsKernelHandle 的指针,并随后卸载回调条目。

0x02 对象回调

对象回调 允许驱动程序根据线程、进程和桌面句柄操作执行自定义代码。它们经常用于自我防御,因为它们提供了保护关键进程不被篡改的便利方式。由于保护是在内核级执行的,即使在用户模式下的提升攻击者也会受到保护。此外,对象回调也对监视和检测可疑活动很有用。

无论用途如何,对象回调都可以使用 ObRegisterCallbacks 例程进行设置。FudModule 自然尝试做正好相反的事情:这就是移除所有已注册的对象回调。这将使其能够绕过自我防御机制,并逃避基于对象回调的检测/遥测。

该根套件技术的实现自之前版本以来保持不变,因此无需详细说明。根套件首先扫描 ObGetObjectType 例程的函数体,以获得 nt!ObTypeIndexTable 的地址。该表包含指向 OBJECTTYPE 结构的指针数组,每个结构代表一个特定的对象类型,例如 Process、Token 或 SymbolicLink。FudModule 遍历该数组跳过前两个特殊含义元素,检查每个 OBJECTTYPECallbackList,该列表包含特定对象类型注册的对象回调的双向链表。根套件随后通过使每个节点的前向和后向指针指向自己,从而清空 CallbackList。

0x04 进程、线程和映像内核回调

以下根套件技术旨在禁用三种更多类型的内核回调:进程、线程 和 映像 回调。顾名思义,每当创建新进程、生成新线程或加载新映像例如,加载 DLL 到进程中时,这些回调会执行内核中的自定义代码。这些回调对于检测恶意活动极为有用。例如,进程回调允许 AV 和 EDR 在即将创建的新进程上执行各种检查。注册这些回调非常简单,仅需将新回调例程作为参数传递给 PsSetCreateProcessNotifyRoutine、PsSetCreateThreadNotifyRoutine 或 PsSetLoadImageNotifyRoutine。这些例程还有其更新的 Ex 变体,甚至在 PsSetCreateProcessNotifyRoutineEx2 的情况下有 Ex2。

进程、线程和映像回调在内核中几乎以相同的方式管理,这使得 FudModule 能够使用基本相同的代码禁用所有三者。我们发现,这段代码自早期版本以来没有变动,主要区别在于新加入了一些不被触动的驱动程序的列表。

FudModule 首先找到 nt!PspNotifyEnableMask、nt!PspLoadImageNotifyRoutine、nt!PspCreateThreadNotifyRoutine 和 nt!PspCreateProcessNotifyRoutine 的地址。这些地址再次通过扫描已导出例程的代码获得,确切的扫描方法根据 Windows 构建版本的不同有所变化。在进行任何修改之前,根套件清除 nt!PspNotifyEnableMask 之后,将睡眠一段时间。这一掩码包含当前启用的回调类型的位字段,因此清除它禁用所有回调。在一些 EDR 绕过技术的情况下就此止步,FudModule 的目标并不是无差别地禁用所有回调,因此对 nt!PspNotifyEnableMask 的修改只是暂时的,最终会恢复为其原始值。我们认为这样做的意图是减少可能导致错误检查的竞争条件的几率。

拉撒路与 FudModule 根套件:超越 BYOVD 的管理员到内核零日漏洞

上述所有的 nt!Psp(LoadImageCreateThreadCreateProcess)NotifyRoutine 全局变量组织为指向 EXCALLBACKROUTINEBLOCK 结构的 EXFASTREF 指针数组至少在 ReactOS 使用的名称。FudModule 在该结构数组中进行遍历,并检查 EXCALLBACKROUTINEBLOCKFunction实际回调例程指针是否在下面列出的模块中的实现。如果是,则该指针将被附加到一个新数组中,该数组用于替换原始数组。这样,实际上移除了所有回调,除了在下表中列出的模块实现的回调。

ntoskrnlexe ahcachesys mmcsssys cngsysksecddsys tcpipsys ioratesys cidlldxgkrnlsys peauthsys wtdsys 允许的内核模块,在移除进程、线程和映像回调期间保留。

0x08 微型过滤驱动程序

文件系统微型过滤器 提供了一种机制,使驱动程序能够拦截文件系统操作。它们被应用于广泛的场景,包括加密、压缩、复制、监控、反病毒扫描或文件系统虚拟化。例如, 加密微型过滤器会在数据写入存储设备之前加密数据,反之则会在数据从存储设备读取时解密数据。FudModule 试图清除所有监控和反病毒微型过滤器,同时保留其他微型过滤器毕竟某些微型过滤器对于保持系统运行至关重要。决定保留哪些微型过滤器以及移除哪些微型过滤器主要基于微型过滤器的高度,这是一个整数值,用于决定如果有多个微型过滤器附加到同一操作时的处理顺序。微软定义的 高度范围 应该被合规的微型过滤器遵循。不幸的是,这些范围也为 FudModule 区分反恶意软件微型过滤器与其他过滤器提供了一个方便的方式。

在之前的版本中,FudModule 通过直接修补其过滤器函数的序言来禁用微型过滤器。在今天,这种做法被认为相当不寻常