Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day

IoT 2个月前 admin
92 0 0

Key Points 关键点

  • Avast discovered an in-the-wild admin-to-kernel exploit for a previously unknown zero-day vulnerability in the appid.sys AppLocker driver. 
    Avast 在 appid.sys AppLocker 驱动程序中发现了一个针对先前未知的零日漏洞的野外管理到内核漏洞。
  • Thanks to Avast’s prompt report, Microsoft addressed this vulnerability as CVE-2024-21338 in the February Patch Tuesday update. 
    由于 Avast 的及时报告,微软在 2 月补丁星期二更新中将此漏洞修复为 CVE-2024-21338。
  • The exploitation activity was orchestrated by the notorious Lazarus Group, with the end goal of establishing a kernel read/write primitive. 
    该利用活动是由臭名昭著的 Lazarus Group 精心策划的,最终目标是建立内核读/写原语。
  • This primitive enabled Lazarus to perform direct kernel object manipulation in an updated version of their data-only FudModule rootkit, a previous version of which was analyzed by ESET and AhnLab
    该原语使 Lazarus 能够在其纯数据 FudModule rootkit 的更新版本中执行直接内核对象操作,ESET 和 AhnLab 分析了该 rootkit 的先前版本。
  • After completely reverse engineering this updated rootkit variant, Avast identified substantial advancements in terms of both functionality and stealth, with four new – and three updated – rootkit techniques. 
    在对这个更新的 Rootkit 变体进行完全逆向工程之后,Avast 发现了在功能和隐秘性方面的重大进步,其中包括四种新的 Rootkit 技术和三种更新的 Rootkit 技术。
  • In a key advancement, the rootkit now employs a new handle table entry manipulation technique in an attempt to suspend PPL (Protected Process Light) protected processes associated with Microsoft Defender, CrowdStrike Falcon, and HitmanPro. 
    一个关键的进步是,rootkit 现在采用了一种新的句柄表条目操作技术,试图暂停与 Microsoft Defender、CrowdStrike Falcon 和 HitmanPro 相关的 PPL(受保护进程轻量级)受保护进程。
  • Another significant step up is exploiting the zero-day vulnerability, where Lazarus previously utilized much noisier BYOVD (Bring Your Own Vulnerable Driver) techniques to cross the admin-to-kernel boundary. 
    另一个重要的进步是利用零日漏洞,Lazarus 之前利用噪音大得多的 BYOVD(自带易受攻击的驱动程序)技术来跨越管理到内核的边界。
  • Avast’s investigation also recovered large parts of the infection chain leading up to the deployment of the rootkit, resulting in the discovery of a new RAT (Remote Access Trojan) attributed to Lazarus. 
    Avast 的调查还恢复了导致 Rootkit 部署的大部分感染链,从而发现了由 Lazarus 发起的新 RAT(远程访问木马)。
  • Technical details concerning the RAT and the initial infection vector will be published in a follow-up blog post, scheduled for release along with our Black Hat Asia 2024 briefing
    有关 RAT 和初始感染媒介的技术细节将在后续博客文章中发布,并计划与我们的 Black Hat Asia 2024 简报一起发布。

Introduction  介绍

When it comes to Windows security, there is a thin line between admin and kernel. Microsoft’s security servicing criteria have long asserted that “[a]dministrator-to-kernel is not a security boundary”, meaning that Microsoft reserves the right to patch admin-to-kernel vulnerabilities at its own discretion. As a result, the Windows security model does not guarantee that it will prevent an admin-level attacker from directly accessing the kernel. This isn’t just a theoretical concern. In practice, attackers with admin privileges frequently achieve kernel-level access by exploiting known vulnerable drivers, in a technique called BYOVD (Bring Your Own Vulnerable Driver). 
当谈到 Windows 安全性时,管理和内核之间只有一线之隔。微软的安全服务标准长期以来一直声称“管理员到内核不是安全边界”,这意味着微软保留自行决定修补管理到内核漏洞的权利。因此,Windows 安全模型不能保证能够阻止管理员级别的攻击者直接访问内核。这不仅仅是一个理论上的问题。在实践中,具有管理员权限的攻击者经常通过利用已知的易受攻击的驱动程序来实现内核级访问,这种技术称为 BYOVD(自带易受攻击的驱动程序)。

Microsoft hasn’t given up on securing the admin-to-kernel boundary though. Quite the opposite, it has made a great deal of progress in making this boundary harder to cross. Defense-in-depth protections, such as DSE (Driver Signature Enforcement) or HVCI (Hypervisor-Protected Code Integrity), have made it increasingly difficult for attackers to execute custom code in the kernel, forcing most to resort to data-only attacks (where they achieve their malicious objectives solely by reading and writing kernel memory). Other defenses, such as driver blocklisting, are pushing attackers to move to exploiting less-known vulnerable drivers, resulting in an increase in attack complexity. Although these defenses haven’t yet reached the point where we can officially call admin-to-kernel a security boundary (BYOVD attacks are still feasible, so calling it one would just mislead users into a false sense of security), they clearly represent steps in the right direction. 
不过,微软并没有放弃保护管理到内核边界的安全。恰恰相反,它在使这一界限更难跨越方面取得了很大进展。深度防御保护,例如 DSE(驱动程序签名强制)或 HVCI(虚拟机管理程序保护的代码完整性),使攻击者越来越难以在内核中执行自定义代码,迫使大多数人求助于纯数据攻击(他们仅通过读写内核内存来实现其恶意目标)。其他防御措施(例如驱动程序阻止列表)正在促使攻击者转向利用鲜为人知的易受攻击的驱动程序,从而导致攻击复杂性增加。尽管这些防御还没有达到我们可以正式将管理到内核称为安全边界的程度(BYOVD 攻击仍然可行,因此称其为安全边界只会误导用户产生错误的安全感),但它们清楚地代表了步骤朝着正确的方向。

From the attacker’s perspective, crossing from admin to kernel opens a whole new realm of possibilities. With kernel-level access, an attacker might disrupt security software, conceal indicators of infection (including files, network activity, processes, etc.), disable kernel-mode telemetry, turn off mitigations, and more. Additionally, as the security of PPL (Protected Process Light) relies on the admin-to-kernel boundary, our hypothetical attacker also gains the ability to tamper with protected processes or add protection to an arbitrary process. This can be especially powerful if lsass is protected with RunAsPPL as bypassing PPL could enable the attacker to dump otherwise unreachable credentials.  
从攻击者的角度来看,从管理到内核的跨越打开了一个全新的可能性领域。通过内核级访问,攻击者可能会破坏安全软件、隐藏感染指标(包括文件、网络活动、进程等)、禁用内核模式遥测、关闭缓解措施等。此外,由于 PPL(受保护进程轻量级)的安全性依赖于管理到内核边界,我们假设的攻击者还获得了篡改受保护进程或为任意进程添加保护的能力。如果 lsass 受到 RunAsPPL 的保护,这可能会特别强大,因为绕过 PPL 可能使攻击者能够转储原本无法访问的凭据。

For more specific examples of what an attacker might want to achieve with kernel-level access, keep reading this blog – in the latter half, we will dive into all the techniques implemented in the FudModule rootkit. 
有关攻击者可能希望通过内核级访问实现什么目的的更具体示例,请继续阅读此博客 – 在后半部分,我们将深入研究 FudModule rootkit 中实现的所有技术。

Living Off the Land: Vulnerable Drivers Edition 
靠土地为生:弱势司机版

With a seemingly growing number of attackers seeking to abuse some of the previously mentioned kernel capabilities, defenders have no choice but to hunt heavily for driver exploits. Consequently, attackers wishing to target well-defended networks must also step up their game if they wish to avoid detection. We can broadly break down admin-to-kernel driver exploits into three categories, each representing a trade-off between attack difficulty and stealth. 
随着越来越多的攻击者试图滥用前面提到的一些内核功能,防御者别无选择,只能大力寻找驱动程序漏洞。因此,想要攻击防御严密的网络的攻击者如果希望避免被发现,也必须加大力度。我们可以将管理到内核的驱动程序漏洞大致分为三类,每一类都代表攻击难度和隐蔽性之间的权衡。

N-Day BYOVD Exploits N 天 BYOVD 漏洞

In the simplest case, an attacker can leverage BYOVD to exploit a publicly known n-day vulnerability. This is very easy to pull off, as there are plenty of public proof-of-concept exploits for various vulnerabilities. However, it’s also relatively straightforward to detect since the attacker must first drop a known vulnerable driver to the file system and then load it into the kernel, resulting in two great detection opportunities. What’s more, some systems may have Microsoft’s vulnerable driver blocklist enabled, which would block some of the most common vulnerable drivers from loading. Previous versions of the FudModule rootkit could be placed in this category, initially exploiting a known vulnerability in dbutil_2_3.sys and then moving on to targeting ene.sys in later versions. 
在最简单的情况下,攻击者可以利用 BYOVD 来利用公开的 n-day 漏洞。这很容易实现,因为有大量针对各种漏洞的公开概念验证利用。然而,检测起来也相对简单,因为攻击者必须首先将已知的易受攻击的驱动程序放入文件系统,然后将其加载到内核中,从而产生两个很好的检测机会。此外,某些系统可能启用了微软的易受攻击的驱动程序阻止列表,这将阻止加载一些最常见的易受攻击的驱动程序。 FudModule rootkit 的早期版本可以归入此类,最初利用 dbutil_2_3.sys 中的已知漏洞,然后在后续版本中转向针对 ene.sys。

Zero-Day BYOVD Exploits 零日 BYOVD 漏洞

In more sophisticated scenarios, an attacker would use BYOVD to exploit a zero-day vulnerability within a signed third-party driver. Naturally, this requires the attacker to first discover such a zero-day vulnerability, which might initially seem like a daunting task. However, note that any exploitable vulnerability in any signed driver will do, and there is unfortunately no shortage of low-quality third-party drivers. Therefore, the difficulty level of discovering such a vulnerability might not be as high as it would initially seem. It might suffice to scan a collection of drivers for known vulnerability patterns, as demonstrated by Carbon Black researchers who recently used bulk static analysis to uncover 34 unique vulnerabilities across more than 200 signed drivers. Such zero-day BYOVD attacks are notably stealthier than n-day attacks since defenders can no longer rely on hashes of known vulnerable drivers for detection. However, some detection opportunities still remain, as loading a random driver represents a suspicious event that might warrant deeper investigation. For an example of an attack belonging to this category, consider the spyware vendor Candiru, which we caught exploiting a zero-day vulnerability in hw.sys for the final privilege escalation stage of their browser exploit chain. 
在更复杂的场景中,攻击者会使用 BYOVD 来利用已签名的第三方驱动程序中的零日漏洞。当然,这要求攻击者首先发现这样的零日漏洞,这最初看起来可能是一项艰巨的任务。但请注意,任何签名驱动程序中的任何可利用漏洞都可以,而且不幸的是,不乏低质量的第三方驱动程序。因此,发现此类漏洞的难度可能不像最初看起来那么高。扫描一组驱动程序以查找已知的漏洞模式可能就足够了,正如 Carbon Black 研究人员最近使用批量静态分析发现 200 多个签名驱动程序中的 34 个独特漏洞所证明的那样。这种零日 BYOVD 攻击明显比 n 天攻击更加隐蔽,因为防御者不能再依赖已知易受攻击的驱动程序的哈希值进行检测。但是,仍然存在一些检测机会,因为加载随机驱动程序代表可疑事件,可能需要进行更深入的调查。对于属于此类别的攻击的示例,请考虑间谍软件供应商 Candiru,我们发现该供应商利用 hw.sys 中的零日漏洞来实现其浏览器漏洞利用链的最终权限升级阶段。

Beyond BYOVD 超越 BYOVD

Finally, the holy grail of admin-to-kernel is going beyond BYOVD by exploiting a zero-day in a driver that’s known to be already installed on the target machine. To make the attack as universal as possible, the most obvious target here would be a built-in Windows driver that’s already a part of the operating system.  
最后,管理到内核的圣杯是通过利用已知已安装在目标计算机上的驱动程序中的零日漏洞来超越 BYOVD。为了使攻击尽可能普遍,这里最明显的目标是已经是操作系统一部分的内置 Windows 驱动程序。

Discovering an exploitable vulnerability in such a driver is significantly more challenging than in the previous BYOVD scenarios for two reasons. First, the number of possible target drivers is vastly smaller, resulting in a much-reduced attack surface. Second, the code quality of built-in drivers is arguably higher than that of random third-party drivers, making vulnerabilities much more difficult to find. It’s also worth noting that – while patching tends to be ineffective at stopping BYOVD attacks (even if a vendor patches their driver, the attacker can still abuse the older, unpatched version of the driver) – patching a built-in driver will make the vulnerability no longer usable for this kind of zero-day attacks. 
由于两个原因,在此类驱动程序中发现可利用的漏洞比以前的 BYOVD 场景更具挑战性。首先,可能的目标驱动程序的数量大大减少,从而大大减少了攻击面。其次,内置驱动程序的代码质量可以说高于随机第三方驱动程序的代码质量,这使得漏洞更难发现。还值得注意的是,虽然修补往往无法有效阻止 BYOVD 攻击(即使供应商修补了他们的驱动程序,攻击者仍然可以滥用旧的、未修补的驱动程序版本),但修补内置驱动程序会导致该漏洞不再可用于此类零日攻击。

If an attacker, despite all of these hurdles, manages to exploit a zero-day vulnerability in a built-in driver, they will be rewarded with a level of stealth that cannot be matched by standard BYOVD exploitation. By exploiting such a vulnerability, the attacker is in a sense living off the land with no need to bring, drop, or load any custom drivers, making it possible for a kernel attack to be truly fileless. This not only evades most detection mechanisms but also enables the attack on systems where driver allowlisting is in place (which might seem a bit ironic, given that CVE-2024-21338 concerns an AppLocker driver).  
如果攻击者克服所有这些障碍,设法利用内置驱动程序中的零日漏洞,他们将获得标准 BYOVD 漏洞无法比拟的隐秘程度。通过利用此类漏洞,攻击者在某种意义上就可以脱离陆地,无需携带、删除或加载任何自定义驱动程序,从而使内核攻击成为真正的无文件攻击。这不仅可以逃避大多数检测机制,还可以对已列入驱动程序白名单的系统进行攻击(考虑到 CVE-2024-21338 涉及 AppLocker 驱动程序,这可能看起来有点讽刺)。

While we can only speculate on Lazarus’ motivation for choosing this third approach for crossing the admin-to-kernel boundary, we believe that stealth was their primary motivation. Given their level of notoriety, they would have to swap vulnerabilities any time someone burned their currently used BYOVD technique. Perhaps they also reasoned that, by going beyond BYOVD, they could minimize the need for swapping by staying undetected for longer. 
虽然我们只能推测 Lazarus 选择第三种方法来跨越管理到内核边界的动机,但我们相信隐秘是他们的主要动机。鉴于他们的恶名程度,每当有人烧毁他们当前使用的 BYOVD 技术时,他们就必须交换漏洞。也许他们还认为,通过超越 BYOVD,他们可以通过保持更长时间不被发现来最大限度地减少交换的需要。

CVE-2024-21338

As far as zero-days go, CVE-2024-21338 is relatively straightforward to both understand and exploit. The vulnerability resides within the IOCTL (Input and Output Control) dispatcher in appid.sys, which is the central driver behind AppLocker, the application whitelisting technology built into Windows. The vulnerable control code 0x22A018 is designed to compute a smart hash of an executable image file. This IOCTL offers some flexibility by allowing the caller to specify how the driver should query and read the hashed file. The problem is, this flexibility is achieved by expecting two kernel function pointers referenced from the IOCTL’s input buffer: one containing a callback pointer to query the hashed file’s size and the other a callback pointer to read the data to be hashed.  
就零日漏洞而言,CVE-2024-21338 相对容易理解和利用。该漏洞存在于 appid.sys 中的 IOCTL(输入和输出控制)调度程序中,它是 AppLocker(Windows 内置的应用程序白名单技术)背后的核心驱动程序。易受攻击的控制代码 0x22A018 旨在计算可执行图像文件的智能哈希值。此 IOCTL 允许调用者指定驱动程序应如何查询和读取散列文件,从而提供了一定的灵活性。问题是,这种灵活性是通过期望从 IOCTL 的输入缓冲区引用两个内核函数指针来实现的:一个包含用于查询哈希文件大小的回调指针,另一个包含用于读取要哈希的数据的回调指针。

Since user mode would typically not be handling kernel function pointers, this design suggests the IOCTL may have been initially designed to be invoked from the kernel. Indeed, while we did not find any legitimate user-mode callers, the IOCTL does get invoked by other AppLocker drivers. For instance, there is a ZwDeviceIoControlFile call in applockerfltr.sys, passing SmpQueryFile and SmpReadFile for the callback pointers. Aside from that, appid.sys itself also uses this functionality, passing AipQueryFileHandle and AipReadFileHandle (which are basically just wrappers over ZwQueryInformationFile and ZwReadFile, respectively). 
由于用户模式通常不会处理内核函数指针,因此这种设计表明 IOCTL 最初可能被设计为从内核调用。事实上,虽然我们没有找到任何合法的用户模式调用者,但 IOCTL 确实被其他 AppLocker 驱动程序调用。例如, applockerfltr.sys 中有一个 ZwDeviceIoControlFile 调用,传递 SmpQueryFile 和 SmpReadFile 作为回调指针。除此之外, appid.sys 本身也使用此功能,传递 AipQueryFileHandle 和 AipReadFileHandle (它们基本上只是 ZwQueryInformationFile 和 ZwReadFile 分别)。

Despite this design, the vulnerable IOCTL remained accessible from user space, meaning that a user-space attacker could abuse it to essentially trick the kernel into calling an arbitrary pointer. What’s more, the attacker also partially controlled the data referenced by the first argument passed to the invoked callback function. This presented an ideal exploitation scenario, allowing the attacker to call an arbitrary kernel function with a high degree of control over the first argument. 
尽管采用了这种设计,但仍然可以从用户空间访问易受攻击的 IOCTL,这意味着用户空间攻击者可以滥用它来欺骗内核调用任意指针。更重要的是,攻击者还部分控制了传递给调用的回调函数的第一个参数引用的数据。这提供了一个理想的利用场景,允许攻击者调用任意内核函数,并对第一个参数进行高度控制。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
A WinDbg session with the triggered vulnerability, traced to the arbitrary callback invocation. Note that the attacker controls both the function pointer to be called (0xdeadbeefdeadbeef in this session) and the data pointed to by the first argument (0xbaadf00dbaadf00d). 
具有触发漏洞的 WinDbg 会话,可追踪到任意回调调用。请注意,攻击者控制要调用的函数指针(此会话中的 0xdeadbeefdeadbeef )和第一个参数指向的数据( 0xbaadf00dbaadf00d )。

If exploitation sounds trivial, note that there are some constraints on what pointers this vulnerability allows an attacker to call. Of course, in the presence of SMEP (Supervisor Mode Execution Prevention), the attacker cannot just supply a user-mode shellcode pointer. What’s more, the callback invocation is an indirect call that may be safeguarded by kCFG (Kernel Control Flow Guard), requiring that the supplied kernel pointers represent valid kCFG call targets. In practice, this does not prevent exploitation, as the attacker can just find some kCFG-compliant gadget function that would turn this into another primitive, such as a (limited) read/write. There are also a few other constraints on the IOCTL input buffer that must be solved in order to reach the vulnerable callback invocation. However, these too are relatively straightforward to satisfy, as the attacker only needs to fake some kernel objects and supply the right values so that the IOCTL handler passes all the necessary checks while at the same time not crashing the kernel. 
如果利用听起来微不足道,请注意,此漏洞允许攻击者调用的指针存在一些限制。当然,在存在 SMEP(管理程序模式执行保护)的情况下,攻击者不能只提供用户模式 ​​shellcode 指针。更重要的是,回调调用是一种间接调用,可以由 kCFG(内核控制流防护)保护,要求提供的内核指针代表有效的 kCFG 调用目标。实际上,这并不能阻止利用,因为攻击者可以找到一些符合 kCFG 的小工具函数,将其转换为另一个原语,例如(有限的)读/写。 IOCTL 输入缓冲区还存在一些其他限制,必须解决这些限制才能实现易受攻击的回调调用。然而,这些也相对容易满足,因为攻击者只需要伪造一些内核对象并提供正确的值,以便 IOCTL 处理程序通过所有必要的检查,同时不会使内核崩溃。

The vulnerable IOCTL is exposed through a device object named \Device\AppIdBreaking down the 0x22A018 control code and extracting the RequiredAccess field reveals that a handle with write access is required to call it. Inspecting the device’s ACL (Access Control List; see the screenshot below), there are entries for local serviceadministrators, and appidsvc. While the entry for administrators does not grant write access, the entry for local service does. Therefore, to describe CVE-2024-21338 more accurately, we should label it local service-to-kernel rather than admin-to-kernel. It’s also noteworthy that appid.sys might create two additional device objects, namely \Device\AppidEDPPlugin and \Device\SrpDevice. Although these come with more permissive ACLs, the vulnerable IOCTL handler is unreachable through them, rendering them irrelevant for exploitation purposes. 
易受攻击的 IOCTL 通过名为 \Device\AppId 的设备对象公开。分解 0x22A018 控制代码并提取 RequiredAccess 字段表明,调用它需要具有写访问权限的句柄。检查设备的 ACL(访问控制列表;请参见下面的屏幕截图),有 local service 、 administrators 和 appidsvc 条目。虽然 administrators 的条目不授予写访问权限,但 local service 的条目却授予写访问权限。因此,为了更准确地描述CVE-2024-21338,我们应该将其标记为本地服务到内核,而不是管理到内核。还值得注意的是 appid.sys 可能会创建两个额外的设备对象,即 \Device\AppidEDPPlugin 和 \Device\SrpDevice 。尽管它们具有更宽松的 ACL,但通过它们无法访问易受攻击的 IOCTL 处理程序,从而使它们与利用目的无关。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
Access control entries of \Device\AppId, revealing that while local service is allowed write access, administrators are not. 
\Device\AppId 的访问控制条目,表明虽然允许 local service 进行写访问,但 administrators 不允许。

As the local service account has reduced privileges compared to administrators, this also gives the vulnerability a somewhat higher impact than standard admin-to-kernel. This might be the reason Microsoft characterized the CVE as Privileges Required: Low, taking into account that local service processes do not always necessarily have to run at higher integrity levels. However, for the purposes of this blog, we still chose to refer to CVE-2024-21338 mainly as an admin-to-kernel vulnerability because we find it better reflects how it was used in the wild – Lazarus was already running with elevated privileges and then impersonated the local service account just prior to calling the IOCTL. 
由于与管理员相比,本地服务帐户的权限较低,因此该漏洞的影响比标准管理到内核的影响要大一些。这可能就是 Microsoft 将 CVE 描述为 Privileges Required: Low 的原因,因为考虑到 local service 进程并不总是必须在更高的完整性级别上运行。然而,就本博客而言,我们仍然选择将 CVE-2024-21338 主要称为管理到内核漏洞,因为我们发现它更好地反映了它在野外的使用方式 – Lazarus 已经以提升的权限运行然后在调用 IOCTL 之前模拟本地服务帐户。

The vulnerability was introduced in Win10 1703 (RS2/15063) when the 0x22A018 IOCTL handler was first implemented. Older builds are not affected as they lack support for the vulnerable IOCTL. Interestingly, the Lazarus exploit bails out if it encounters a build older than Win10 1809 (RS5/17763), completely disregarding three perfectly vulnerable Windows versions. As for the later versions, the vulnerability extended all the way up to the most recent builds, including Win11 23H2. There have been some slight changes to the IOCTL, including an extra argument expected in the input buffer, but nothing that would prevent exploitation.  
该漏洞是在 Win10 1703 (RS2/15063) 首次实现 0x22A018 IOCTL 处理程序时引入的。较旧的版本不受影响,因为它们缺乏对易受攻击的 IOCTL 的支持。有趣的是,如果 Lazarus 漏洞遇到早于 Win10 1809 (RS5/17763) 的版本,它就会逃脱,完全忽略三个完全易受攻击的 Windows 版本。至于后续版本,该漏洞一直延伸到最新版本,包括Win11 23H2。 IOCTL 有一些细微的变化,包括输入缓冲区中预期的额外参数,但没有什么可以阻止利用。

We developed a custom PoC (Proof of Concept) exploit and submitted it in August 2023 as part of a vulnerability report to Microsoft, leading to an advisory for CVE-2024-21338 in the February Patch Tuesday update. The update addressed the vulnerability by adding an ExGetPreviousMode check to the IOCTL handler (see the patch below). This aims to prevent user-mode initiated IOCTLs from triggering the arbitrary callbacks. 
我们开发了一个自定义 PoC(概念验证)漏洞,并于 2023 年 8 月将其作为漏洞报告的一部分提交给 Microsoft,从而在 2 月补丁星期二更新中发布了 CVE-2024-21338 的通报。该更新通过向 IOCTL 处理程序添加 ExGetPreviousMode 检查来解决该漏洞(请参阅下面的补丁)。这样做的目的是防止用户模式启动的 IOCTL 触发任意回调。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
The patched IOCTL handler. If feature 2959575357 is enabled, attempts to call the IOCTL with PreviousMode==UserMode should immediately result in STATUS_INVALID_DEVICE_REQUEST, failing to even reach AipSmartHashImageFile
修补后的 IOCTL 处理程序。如果启用了 2959575357 功能,则尝试使用 PreviousMode==UserMode 调用 IOCTL 应立即导致 STATUS_INVALID_DEVICE_REQUEST ,甚至无法到达 AipSmartHashImageFile 。

Though the vulnerability may only barely meet Microsoft’s security servicing criteria, we believe patching was the right choice and would like to thank Microsoft for eventually addressing this issue. Patching will undoubtedly disrupt Lazarus’ offensive operations, forcing them to either find a new admin-to-kernel zero-day or revert to using BYOVD techniques. While discovering an admin-to-kernel zero-day may not be as challenging as discovering a zero-day in a more attractive attack surface (such as standard user-to-kernel, or even sandbox-to-kernel), we believe that finding one would still require Lazarus to invest significant resources, potentially diverting their focus from attacking some other unfortunate targets. 
尽管该漏洞可能仅勉强满足微软的安全服务标准,但我们相信修补是正确的选择,并感谢微软最终解决了这个问题。修补无疑会扰乱 Lazarus 的攻击行动,迫使他们要么寻找新的管理到内核零日漏洞,要么恢复使用 BYOVD 技术。虽然发现管理员到内核的零日漏洞可能不像在更具吸引力的攻击面(例如标准用户到内核,甚至沙箱到内核)中发现零日漏洞那么具有挑战性,但我们相信找到一个仍然需要拉撒路投入大量资源,这可能会分散他们攻击其他一些不幸目标的注意力。

Exploitation  开发

The Lazarus exploit begins with an initialization stage, which performs a one-time setup for both the exploit and the rootkit (both have been compiled into the same module). This initialization starts by dynamically resolving all necessary Windows API functions, followed by a low-effort anti-debug check on PEB.BeingDebugged. Then, the exploit inspects the build number to see if it’s running on a supported Windows version. If so, it loads hardcoded constants tailored to the current build. Interestingly, the choice of constants sometimes comes down to the update build revision (UBR), showcasing a high degree of dedication towards ensuring that the code runs cleanly across a wide range of target machines.  
Lazarus 漏洞利用从初始化阶段开始,该阶段对漏洞利用程序和 rootkit 执行一次性设置(两者都已编译到同一模块中)。此初始化首先动态解析所有必需的 Windows API 函数,然后对 PEB.BeingDebugged 进行轻松的反调试检查。然后,该漏洞利用程序会检查内部版本号,以查看它是否在受支持的 Windows 版本上运行。如果是这样,它会加载针对当前版本定制的硬编码常量。有趣的是,常量的选择有时取决于更新构建修订版 (UBR),这体现了对确保代码在各种目标机器上干净运行的高度奉献。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
A decompiled code snippet, loading version-specific hardcoded constants. This particular example contains offsets and syscall numbers for Win10 1809. 
反编译的代码片段,加载特定于版本的硬编码常量。此特定示例包含 Win10 1809 的偏移量和系统调用号。

The initialization process then continues with leaking the base addresses of three kernel modules: ntoskrnlnetio, and fltmgr. This is achieved by calling NtQuerySystemInformation using the SystemModuleInformation class. The KTHREAD address of the currently executing thread is also leaked in a similar fashion, by duplicating the current thread pseudohandle and then finding the corresponding kernel object address using the SystemExtendedHandleInformation system information class. Finally, the exploit manually loads the ntoskrnl image into the user address space, only to scan for relative virtual addresses (RVAs) of some functions of interest. 
然后,初始化过程继续泄漏三个内核模块的基地址: ntoskrnl 、 netio 和 fltmgr 。这是通过使用 SystemModuleInformation 类调用 NtQuerySystemInformation 来实现的。当前执行线程的 KTHREAD 地址也以类似的方式泄漏,通过复制当前线程伪句柄,然后使用 SystemExtendedHandleInformation 系统信息类查找相应的内核对象地址。最后,该漏洞利用手动将 ntoskrnl 映像加载到用户地址空间中,仅扫描某些感兴趣函数的相对虚拟地址 (RVA)。

Since the appid.sys driver does not have to be already loaded on the target machine, the exploit may first have to load it itself. It chooses to accomplish this in an indirect way, by writing an event to one specific AppLocker-related ETW (Event Tracing for Windows) provider. Once appid.sys is loaded, the exploit impersonates the local service account using a direct syscall to NtSetInformationThread with the ThreadImpersonationToken thread information class. By impersonating local service, it can now obtain a read/write handle to \Device\AppId. With this handle, the exploit finally prepares the IOCTL input buffer and triggers the vulnerability using the NtDeviceIoControlFile syscall.  
由于 appid.sys 驱动程序不必已加载到目标计算机上,因此漏洞利用程序可能首先必须加载它本身。它选择以间接方式完成此操作,即向一个特定的 AppLocker 相关 ETW(Windows 事件跟踪)提供程序写入事件。加载 appid.sys 后,该漏洞利用通过 ThreadImpersonationToken 线程信息类对 NtSetInformationThread 进行直接系统调用来模拟 local service 帐户。通过模拟 local service ,它现在可以获得 \Device\AppId 的读/写句柄。有了这个句柄,漏洞利用程序最终准备好 IOCTL 输入缓冲区,并使用 NtDeviceIoControlFile 系统调用触发漏洞。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
Direct syscalls are heavily used throughout the exploit. 

The exploit crafts the IOCTL input buffer in such a way that the vulnerable callback is essentially a gadget that performs a 64-bit copy from the IOCTL input buffer to an arbitrary target address. This address was chosen to corrupt the PreviousMode of the current thread. By ensuring the corresponding source byte in the IOCTL input buffer is zero, the copy will clear the PreviousMode field, effectively resulting in its value being interpreted as KernelMode. Targeting PreviousMode like this is a widely popular exploitation technique, as corrupting this one byte in the KTHREAD structure bypasses kernel-mode checks inside syscalls such as NtReadVirtualMemory or NtWriteVirtualMemory, allowing a user-mode attacker to read and write arbitrary kernel memory. Note that while this technique was mitigated on some Windows Insider Builds, this mitigation has yet to reach general availability at the time of writing.

Interestingly, the exploit may attempt to trigger the vulnerable IOCTL twice. This is due to an extra argument that was added in Win11 22H2. As a result, the IOCTL handler on newer builds expects the input buffer to be 0x20 bytes in size while, previously, the expected size was only 0x18. Rather than selecting the proper input buffer size for the current build, the exploit just tries calling the IOCTL twice: first with an input buffer size 0x18 then – if not successful – with 0x20. This is a valid approach since the IOCTL handler’s first action is to check the input buffer size, and if it doesn’t match the expected size, it would just immediately return STATUS_INVALID_PARAMETER.

To check if it was successful, the exploit employs the NtWriteVirtualMemory syscall, attempting to read the current thread’s PreviousMode (Lazarus avoids using NtReadVirtualMemory, more on this later). If the exploit succeeded, the syscall should return STATUS_SUCCESS, and the leaked PreviousMode byte should equal 0 (meaning KernelMode). Otherwise, the syscall should return an error status code, as it should be impossible to read kernel memory without a corrupted PreviousMode.

In our exploit analysis, we deliberately chose to omit some key details, such as the choice of the callback gadget function. This decision was made to strike the right balance between helping defenders with detection but not making exploitation too widely accessible. For those requiring more information for defensive purposes, we may be able to share additional details on a case-by-case basis. 
在我们的漏洞分析中,我们故意选择省略一些关键细节,例如回调小工具函数的选择。做出这一决定是为了在帮助防御者进行检测和不让漏洞利用过于广泛之间取得适当的平衡。对于那些出于防御目的需要更多信息的人,我们也许可以根据具体情况分享更多详细信息。

The FudModule Rootkit FudModule Rootkit

The entire goal of the admin-to-kernel exploit was to corrupt the current thread’s PreviousMode. This allows for a powerful kernel read/write primitive, where the affected user-mode thread can read and write arbitrary kernel memory using the Nt(Read|Write)VirtualMemory syscalls. Armed with this primitive, the FudModule rootkit employs direct kernel object manipulation (DKOM) techniques to disrupt various kernel security mechanisms. It’s worth reiterating that FudModule is a data-only rootkit, meaning it executes entirely from user space and all the kernel tampering is performed through the read/write primitive.  
管理到内核漏洞的整个目标是破坏当前线程的 PreviousMode 。这允许强大的内核读/写原语,其中受影响的用户模式线程可以使用 Nt(Read|Write)VirtualMemory 系统调用读取和写入任意内核内存。有了这个原语,FudModule rootkit 采用直接内核对象操作 (DKOM) 技术来破坏各种内核安全机制。值得重申的是,FudModule 是一个纯数据 rootkit,这意味着它完全从用户空间执行,所有内核篡改都是通过读/写原语执行的。

The first variants of the FudModule rootkit were independently discovered by AhnLab and ESET research teams, with both publishing detailed analyses in September 2022. The rootkit was named after the FudModule.dll string used as the name in its export table. While this artifact is not present anymore, there is no doubt that what we found is an updated version of the same rootkit. AhnLab’s report documented a sample from early 2022, which incorporated seven data-only rootkit techniques and was enabled through a BYOVD exploit for ene.sys. ESET’s report examined a slightly earlier variant from late 2021, also featuring seven rootkit techniques but exploiting a different BYOVD vulnerability in dbutil_2_3.sys. In contrast, our discovery concerns a sample featuring nine rootkit techniques and exploiting a previously unknown admin-to-kernel vulnerability. Out of these nine techniques, four are new, three are improved, and two remain unchanged from the previous variants. This leaves two of the original seven techniques, which have been deprecated and are no longer present in the latest variant. 
FudModule rootkit 的第一个变体由 AhnLab 和 ESET 研究团队独立发现,并于 2022 年 9 月发布了详细分析。该 rootkit 以其导出表中用作名称的 FudModule.dll 字符串命名。虽然此工件已不再存在,但毫无疑问我们发现的是同一 rootkit 的更新版本。 AhnLab 的报告记录了 2022 年初的一个样本,其中包含七种纯数据 rootkit 技术,并通过 ene.sys 的 BYOVD 漏洞启用。 ESET 的报告检查了 2021 年底的一个稍早的变体,该变体也具有七种 Rootkit 技术,但利用了 dbutil_2_3.sys 中的不同 BYOVD 漏洞。相比之下,我们的发现涉及一个具有九种 Rootkit 技术并利用以前未知的管理到内核漏洞的样本。在这九种技术中,有四种是新的,三种是改进的,两种与以前的变体保持不变。这留下了最初的七种技术中的两种,这些技术已被弃用,并且不再出现在最新的变体中。

Each rootkit technique is assigned a bit, ranging from 0x1 to 0x200 (the 0x20 bit is left unused in the current variant). FudModule executes the techniques sequentially, in an ascending order of the assigned bits. The bits are used to report on the success of the individual techniques. During execution, FudModule will construct an integer value (named bitfield_techniques in the decompilation below), where only the bits corresponding to successfully executed techniques will be set. This integer is ultimately written to a file named tem1245.tmp, reporting on the rootkit’s success. Interestingly, we did not find this filename referenced in any other Lazarus sample, suggesting the dropped file is only inspected through hands-on-keyboard activity, presumably through a RAT (Remote Access Trojan) command. This supports our beliefs that FudModule is only loosely integrated into the rest of Lazarus’ malware ecosystem and that Lazarus is very careful about using the rootkit, only deploying it on demand under the right circumstances. 
每个 Rootkit 技术都分配了一个位,范围从 0x1 到 0x200 ( 0x20 位在当前变体中未使用)。 FudModule 按照指定位的升序顺序执行这些技术。这些位用于报告各个技术的成功。在执行过程中,FudModule 将构造一个整数值(在下面的反编译中名为 bitfield_techniques ),其中仅设置与成功执行的技术相对应的位。该整数最终被写入名为 tem1245.tmp 的文件中,报告 rootkit 的成功。有趣的是,我们没有发现任何其他 Lazarus 样本中引用了该文件名,这表明仅通过手动键盘活动(可能是通过 RAT(远程访问木马)命令)来检查删除的文件。这支持了我们的信念,即 FudModule 只是松散地集成到 Lazarus 恶意软件生态系统的其余部分中,并且 Lazarus 对使用 rootkit 非常谨慎,仅在正确的情况下按需部署它。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
The rootkit’s “main” function, executing the individual rootkit techniques. Note the missing 0x20 technique. 
Rootkit 的“主要”功能,执行各个 Rootkit 技术。请注意缺少的 0x20 技术。

Based on the large number of updates, it seems that FudModule remains under active development. The latest variant appears more robust, avoiding some potentially problematic practices from the earlier variants. Since some techniques target undocumented kernel internals in a way that we have not previously encountered, we believe that Lazarus must be conducting their own kernel research. Further, though the rootkit is certainly technically sophisticated, we still identified a few bugs here and there. These may either limit the rootkit’s intended functionality or even cause kernel bug checks under the right conditions. While we find some of these bugs very interesting and would love to share the details, we do not enjoy the idea of providing free bug reports to threat actors, so we will hold onto them for now and potentially share some information later if the bugs get fixed. 
基于大量的更新,FudModule 似乎仍在积极开发中。最新的变体看起来更强大,避免了早期变体中一些潜在问题的做法。由于某些技术以我们以前从未遇到过的方式针对未记录的内核内部结构,因此我们相信 Lazarus 一定在进行他们自己的内核研究。此外,尽管 Rootkit 在技术上确实很复杂,但我们仍然发现了一些错误。这些可能会限制 Rootkit 的预期功能,甚至会在适当的条件下导致内核错误检查。虽然我们发现其中一些错误非常有趣并且愿意分享详细信息,但我们不喜欢向威胁行为者提供免费错误报告的想法,因此我们将暂时保留它们,并可能在错误出现后稍后分享一些信息固定的。

Interestingly, FudModule utilizes the NtWriteVirtualMemory syscall for both reading and writing kernel memory, eliminating the need to call NtReadVirtualMemory. This leverages the property that, when limited to a single virtual address space, NtReadVirtualMemory and NtWriteVirtualMemory are basically inverse operations with respect to the values of the source Buffer and the destination BaseAddress arguments. In other words, writing to kernel memory can be thought of as writing from a user-mode Buffer to a kernel-mode BaseAddress, while reading from kernel memory could be conversely achieved by swapping arguments, that is writing from a kernel-mode Buffer to a user-mode BaseAddress. Lazarus’ implementation takes advantage of this, which seems to be an intentional design decision since most developers would likely prefer the more straightforward way of using NtReadVirtualMemory for reading kernel memory and NtWriteVirtualMemory for writing kernel memory. We can only guess why Lazarus chose this approach, but this might be yet another stealth-enhancing feature. With their implementation, they only must use one suspicious syscall instead of two, potentially reducing the number detection opportunities.

Debug Prints

Before we delve into the actual rootkit techniques, there is one last thing worth discussing. To our initial surprise, Lazarus left a handful of plaintext debug prints in the compiled code. Such prints are typically one of the best things that can happen to a malware researcher, because they tend to accelerate the reverse engineering process significantly. In this instance, however, some of the prints had the opposite effect, sometimes even making us question if we understood the code correctly.  
在我们深入研究实际的 Rootkit 技术之前,还有最后一件事值得讨论。令我们最初惊讶的是,Lazarus 在编译后的代码中留下了一些纯文本调试打印。对于恶意软件研究人员来说,此类打印通常是最好的事情之一,因为它们往往会显着加速逆向工程过程。然而,在这种情况下,一些印刷品产生了相反的效果,有时甚至让我们怀疑我们是否正确理解了代码。

As an example, let us mention the string get rop function addresses failed. Assuming rop stands for return-oriented programming, this string would make perfect sense in the context of exploitation, if not for the fact that not a single return address was corrupted in the exploit.  
作为示例,让我们提及字符串 get rop function addresses failed 。假设 rop 代表面向返回的编程,如果不是因为漏洞利用中没有一个返回地址被损坏,那么这个字符串在漏洞利用的上下文中将非常有意义。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
Plaintext debug strings found in the rootkit. The term vaccine is used to refer to security software. 
在 rootkit 中找到的纯文本调试字符串。疫苗一词用于指安全软件。

While written in English, the debug strings suggest their authors are not native speakers, occasionally even pointing to their supposed Korean origin. This is best seen on the frequent usage of the term vaccine throughout the rootkit. This had us scratching our heads at first, because it was unclear how vaccines would relate to the rootkit functionality. However, it soon became apparent that the term was used to refer to security software. This might originate from a common Korean translation of antivirus (바이러스 백신), a compound word with the literal meaning virus vaccine. Note that even North Korea’s “own” antivirus was called SiliVaccine, and to the best of our knowledge, the term vaccine would not be used like this in other languages such as Japanese. Additionally, this is not the first time Korean-speaking threat actors have used this term. For instance, AhnLab’s recent report on Kimsuky mentions the following telltale command: 
虽然用英语编写,但调试字符串表明其作者不是母语人士,有时甚至指出其所谓的韩国血统。从整个 Rootkit 中频繁使用术语“疫苗”可以看出这一点。一开始这让我们摸不着头脑,因为尚不清楚疫苗与 Rootkit 功能有何关系。然而,很快人们就发现该术语是用来指代安全软件的。这可能源自韩语中常见的防病毒翻译(바 Shoulder스 백신),这是一个复合词,字面意思是病毒疫苗。请注意,即使是朝鲜“自己的”防病毒软件也被称为 SiliVaccine,据我们所知,“疫苗”一词在日语等其他语言中不会这样使用。此外,这并不是韩语威胁行为者第一次使用该术语。例如,AhnLab 最近关于 Kimsuky 的报告提到了以下明显的命令:

 
cmd.exe /U /c wmic /namespace:\\root\securitycenter2 path antivirusproduct get displayname > vaccine.txt

Another puzzle is the abbreviation pvmode, which we believe refers to PreviousMode. A Google search for pvmode yields exactly zero relevant results, and we suspect most English speakers would choose different abbreviations, such as prvmode or prevmode. However, after consulting this with language experts, we learned that using the abbreviation pvmode would be unusual for Korean speakers too. 
另一个难题是缩写 pvmode ,我们认为它指的是 PreviousMode 。 Google 搜索 pvmode 产生的相关结果恰好为零,我们怀疑大多数说英语的人会选择不同的缩写,例如 prvmode 或 prevmode 。然而,在咨询了语言专家后,我们了解到使用缩写 pvmode 对于韩语使用者来说也是不常见的。

Finally, there is also the debug message disableV3Protection passed. Judging from the context, the rather generic term V3 here refers to AhnLab V3 Endpoint Security. Considering the geopolitical situation, North Korean hacker groups are likely well-acquainted with South Korean AhnLab, so it would make perfect sense that they internally refer to them using such a non-specific shorthand. 
最后,还有调试消息 disableV3Protection passed 。从上下文来看,这里相当通用的术语 V3 指的是 AhnLab V3 Endpoint Security。考虑到地缘政治局势,朝鲜黑客组织可能对韩国 AhnLab 非常熟悉,因此他们在内部使用这种非特定的速记方式来称呼它们是完全有道理的。

0x01 – Registry Callbacks 
0x01 – 注册表回调

The first rootkit technique is designed to address registry callbacks. This is a documented Windows mechanism which allows security solutions to monitor registry operations. A security solution’s kernel-mode component can call the CmRegisterCallbackEx routine to register a callback, which gets notified whenever a registry operation is performed on the system. What’s more, since the callback is invoked synchronously, before (or after) the actual operation is performed, the callback can even block or modify forbidden/malicious operations. FudModule’s goal here is to remove existing registry callbacks and thus disrupt security solutions that rely on this mechanism. 
第一种 Rootkit 技术旨在解决注册表回调问题。这是一个记录在案的 Windows 机制,允许安全解决方案监视注册表操作。安全解决方案的内核模式组件可以调用 CmRegisterCallbackEx 例程来注册回调,每当在系统上执行注册表操作时都会收到通知。更重要的是,由于回调是同步调用的,在执行实际操作之前(或之后),回调甚至可以阻止或修改禁止/恶意操作。 FudModule 的目标是删除现有的注册表回调,从而破坏依赖此机制的安全解决方案。

The callback removal itself is performed by directly modifying some internal data structures managed by the kernel. This was also the case in the previous version, as documented by ESET and AhnLab. There, the rootkit found the address of nt!CallbackListHead (which contains a doubly linked, circular list of all existing registry callbacks) and simply emptied it by pointing it to itself. 
回调移除本身是通过直接修改内核管理的一些内部数据结构来执行的。正如 ESET 和 AhnLab 所记录的那样,以前的版本也是如此。在那里,rootkit 找到了 nt!CallbackListHead 的地址(其中包含所有现有注册表回调的双向链接循环列表),并通过将其指向自身来清空它。

In the current version of FudModule, this technique was improved to leave some selected callbacks behind, perhaps making the rootkit stealthier. This updated version starts the same as the previous one: by finding the address of nt!CallbackListHead. This is done by resolving CmUnRegisterCallback (this resolution is performed by name, through iterating over the export table of ntoskrnl in memory), scanning its function body for the lea rcx,[nt!CallbackListHead] instruction, and then calculating the final address from the offset extracted from the instruction’s opcodes. 
在当前版本的 FudModule 中,该技术得到了改进,留下了一些选定的回调,这或许使得 rootkit 更加隐蔽。此更新版本的启动方式与前一个版本相同:通过查找 nt!CallbackListHead 的地址。这是通过解析 CmUnRegisterCallback (此解析是通过名称执行的,通过迭代内存中 ntoskrnl 的导出表),扫描其函数体以查找 lea rcx,[nt!CallbackListHead] 指令,然后根据从指令操作码中提取的偏移量计算最终地址。

With the nt!CallbackListHead address, FudModule can iterate over the registry callback linked list. It inspects each entry and determines if the callback routine is implemented in ntoskrnl.exeapplockerfltr.sys, or bfs.sys. If it is, the callback is left untouched. Otherwise, the rootkit replaces the callback routine pointer with a pointer to ObIsKernelHandle and then proceeds to unlink the callback entry. 
通过 nt!CallbackListHead 地址,FudModule可以迭代注册表回调链表。它检查每个条目并确定回调例程是否在 ntoskrnl.exe 、 applockerfltr.sys 或 bfs.sys 中实现。如果是,则回调保持不变。否则,rootkit 将用指向 ObIsKernelHandle 的指针替换回调例程指针,然后继续取消回调条目的链接。

0x02 – Object Callbacks 
0x02 – 对象回调

Object callbacks allow drivers to execute custom code in response to thread, process, and desktop handle operations. They are often used in self-defense, as they represent a convenient way to protect critical processes from being tampered with. Since the protection is enforced at the kernel level, this should protect even against elevated attackers, as long as they stay in user mode. Alternatively, object callbacks are also useful for monitoring and detecting suspicious activity.  
对象回调允许驱动程序执行自定义代码以响应线程、进程和桌面句柄操作。它们经常用于自卫,因为它们代表了保护关键进程不被篡改的便捷方法。由于保护是在内核级别强制执行的,因此即使是高级攻击者,只要他们保持在用户模式,就应该可以保护他们。另外,对象回调对于监视和检测可疑活动也很有用。

Whatever the use case, object callbacks can be set up using the ObRegisterCallbacks routine. FudModule naturally attempts to do the exact opposite: that is to remove all registered object callbacks. This could let it bypass self-defense mechanisms and evade object callback-based detection/telemetry. 
无论使用情况如何,都可以使用 ObRegisterCallbacks 例程设置对象回调。 FudModule 自然会尝试做完全相反的事情:即删除所有已注册的对象回调。这可以让它绕过自卫机制并逃避基于对象回调的检测/遥测。

The implementation of this rootkit technique has stayed the same since the previous version, so there is no need to go into too much detail. First, the rootkit scans the body of the ObGetObjectType routine to obtain the address of nt!ObTypeIndexTable. This contains an array of pointers to _OBJECT_TYPE structures, each of which represents a distinct object type, such as ProcessToken, or SymbolicLink. FudModule iterates over this array (skipping the first two special-meaning elements) and inspects each _OBJECT_TYPE.CallbackList, which contains a doubly linked list of object callbacks registered for the particular object type. The rootkit then empties the CallbackList by making each node’s forward and backward pointer point to itself. 
该 Rootkit 技术的实现自上一版本以来一直保持不变,因此无需讨论太多细节。首先,rootkit 扫描 ObGetObjectType 例程的主体以获取 nt!ObTypeIndexTable 的地址。它包含指向 _OBJECT_TYPE 结构的指针数组,每个结构都代表不同的对象类型,例如 Process 、 Token 或 SymbolicLink . FudModule 迭代此数组(跳过前两个特殊含义的元素)并检查每个 _OBJECT_TYPE.CallbackList ,其中包含为特定对象类型注册的对象回调的双向链接列表。然后,rootkit 通过使每个节点的前向和后向指针指向自身来清空 CallbackList 。

0x04 – Process, Thread, and Image Kernel Callbacks 
0x04 – 进程、线程和图像内核回调

This next rootkit technique is designed to disable three more types of kernel callbacks: processthread, and image callbacks. As their names suggest, these are used to execute custom kernel code whenever a new process is created, a new thread spawned, or a new image loaded (e.g. a DLL loaded into a process). These callbacks are extremely useful for detecting malicious activity. For instance, process callbacks allow AVs and EDRs to perform various checks on each new process that is to be created. Registering these callbacks is very straightforward. All that is needed is to pass the new callback routine as an argument to PsSetCreateProcessNotifyRoutinePsSetCreateThreadNotifyRoutine, or PsSetLoadImageNotifyRoutine. These routines also come in their updated Ex variants, or even Ex2 in the case of PsSetCreateProcessNotifyRoutineEx2
下一个 Rootkit 技术旨在禁用另外三种类型的内核回调:进程、线程和图像回调。顾名思义,它们用于在创建新进程、生成新线程或加载新映像(例如加载到进程中的 DLL)时执行自定义内核代码。这些回调对于检测恶意活动非常有用。例如,进程回调允许 AV 和 EDR 对要创建的每个新进程执行各种检查。注册这些回调非常简单。所需要做的就是将新的回调例程作为参数传递给 PsSetCreateProcessNotifyRoutine 、 PsSetCreateThreadNotifyRoutine 或 PsSetLoadImageNotifyRoutine 。这些例程也有更新的 Ex 变体,甚至是 PsSetCreateProcessNotifyRoutineEx2 的 Ex2 。

Process, thread, and image callbacks are managed by the kernel in an almost identical way, which allows FudModule to use essentially the same code to disable all three of them. We find that this code has not changed much since the previous version, with the main difference being new additions to the list of drivers whose callbacks are left untouched.

FudModule first finds the addresses of nt!PspNotifyEnableMasknt!PspLoadImageNotifyRoutinent!PspCreateThreadNotifyRoutine, and nt!PspCreateProcessNotifyRoutine. These are once again obtained by scanning the code of exported routines, with the exact scanning method subject to some variation based on the Windows build number. Before any modification is performed, the rootkit clears nt!PspNotifyEnableMask and sleeps for a brief amount of time. This mask contains a bit field of currently enabled callback types, so clearing it disables all callbacks. While some EDR bypasses would stop here, FudModule’s goal is not to disable all callbacks indiscriminately, so the modification of nt!PspNotifyEnableMask is only temporary, and FudModule eventually restores it back to its original value. We believe the idea behind this temporary modification is to decrease the chance of a race condition that could potentially result in a bug check.

All three of the above nt!Psp(LoadImage|CreateThread|CreateProcess)NotifyRoutine globals are organized as an array of _EX_FAST_REF pointers to _EX_CALLBACK_ROUTINE_BLOCK structures (at least that’s the name used in ReactOS, Microsoft does not share a symbol name here). FudModule iterates over all these structures and checks if _EX_CALLBACK_ROUTINE_BLOCK.Function (the actual callback routine pointer) is implemented in one of the below-whitelisted modules. If it is, the pointer will get appended to a new array that will be used to replace the original one. This effectively removes all callbacks except for those implemented in one of the below-listed modules.

ntoskrnl.exe  ahcache.sys  mmcss.sys  cng.sys 
ksecdd.sys  tcpip.sys  iorate.sys  ci.dll 
dxgkrnl.sys  peauth.sys  wtd.sys
Kernel modules that are allowed during the removal of process, thread, and image callbacks. 
删除进程、线程和图像回调过程中允许的内核模块。

0x08 – Minifilter Drivers 
0x08 – 微过滤器驱动程序

File system minifilters provide a mechanism for drivers to intercept file system operations. They are used in a wide range of scenarios, including encryption, compression, replication, monitoring, antivirus scanning, or file system virtualization. For instance, an encryption minifilter would encrypt the data before it is written to the storage device and, conversely, decrypt the data after it is read. FudModule is trying to get rid of all the monitoring and antivirus minifilters while leaving the rest untouched (after all, some minifilters are crucial to keep the system running). The choice about which minifilters to keep and which to remove is based mainly on the minifilter’s altitude, an integer value that is used to decide the processing order in case there are multiple minifilters attached to the same operation. Microsoft defines altitude ranges that should be followed by well-behaved minifilters. Unfortunately, these ranges also represent a very convenient way for FudModule to distinguish anti-malware minifilters from the rest. 
文件系统微过滤器为驱动程序提供了拦截文件系统操作的机制。它们用于多种场景,包括加密、压缩、复制、监控、防病毒扫描或文件系统虚拟化。例如,加密微过滤器会在将数据写入存储设备之前对数据进行加密,反之,在读取数据后对数据进行解密。 FudModule 试图摆脱所有监控和防病毒微过滤器,同时保持其余部分不变(毕竟,一些微过滤器对于保持系统运行至关重要)。选择保留哪些微过滤器以及删除哪些微过滤器主要基于微过滤器的高度,这是一个整数值,用于决定处理顺序,以防有多个微过滤器附加到同一操作。 Microsoft 定义了行为良好的微过滤器应遵循的海拔范围。不幸的是,这些范围也代表了 FudModule 区分反恶意软件微过滤器与其他微过滤器的一种非常方便的方法。

In its previous version, FudModule disabled minifilters by directly patching their filter functions’ prologues. This would be considered very unusual today, with HVCI (Hypervisor-Protected Code Integrity) becoming more prevalent, even turned on by default on Windows 11. Since HVCI is a security feature designed to prevent the execution of arbitrary code in the kernel, it would stand in the way of FudModule trying to patch the filter function. This forced Lazarus to completely reimplement this rootkit technique, so the current version of FudModule disables file system minifilters in a brand-new data-only attack. 
在之前的版本中,FudModule 通过直接修补过滤器函数的序言来禁用微过滤器。这在今天被认为是非常不寻常的,因为 HVCI(虚拟机管理程序保护的代码完整性)变得越来越普遍,甚至在 Windows 11 上默认打开。由于 HVCI 是一项旨在防止在内核中执行任意代码的安全功能,因此它会阻止 FudModule 尝试修补过滤器功能。这迫使 Lazarus 完全重新实现这种 rootkit 技术,因此当前版本的 FudModule 在全新的纯数据攻击中禁用了文件系统微过滤器。

This attack starts by resolving FltEnumerateFilters and using it to find FltGlobals.FrameList.rList. This is a linked list of FLTMGR!_FLTP_FRAME structures, each representing a single filter manager frame. From here, FudModule follows another linked list at _FLTP_FRAME.AttachedVolumes.rList. This linked list consists of FLTMGR!_FLT_VOLUME structures, describing minifilters attached to a particular file system volume. Interestingly, the rootkit performs a sanity check to make sure that the pool tag associated with the _FLT_VOLUME allocation is equal to FMvo. With the sanity check satisfied, FudModule iterates over _FLT_VOLUME.Callbacks.OperationsLists, which is an array of linked lists of FLTMGR!_CALLBACK_NODE structures, indexed by IRP major function codes. For instance, OperationsLists[IRP_MJ_READ] is a linked list describing all filters attached to the read operation on a particular volume. 
此攻击首先解析 FltEnumerateFilters 并使用它来查找 FltGlobals.FrameList.rList 。这是一个 FLTMGR!_FLTP_FRAME 结构的链接列表,每个结构代表一个过滤器管理器框架。从这里开始,FudModule 遵循 _FLTP_FRAME.AttachedVolumes.rList 处的另一个链接列表。该链表由 FLTMGR!_FLT_VOLUME 结构组成,描述附加到特定文件系统卷的微过滤器。有趣的是,rootkit 会执行健全性检查,以确保与 _FLT_VOLUME 分配关联的池标记等于 FMvo 。满足完整性检查后,FudModule 迭代 _FLT_VOLUME.Callbacks.OperationsLists ,它是 FLTMGR!_CALLBACK_NODE 结构的链表数组,由 IRP 主函数代码索引。例如, OperationsLists[IRP_MJ_READ] 是一个链接列表,描述附加到特定卷上的 read 操作的所有过滤器。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
FudModule making sure the pool tag of a _FLT_VOLUME chunk is equal to FMvo

For each _CALLBACK_NODE, FudModule obtains the corresponding FLTMGR!_FLT_INSTANCE and FLTMGR!_FLT_FILTER structures and uses them to decide whether to unlink the callback node. The first check is based on the name of the driver behind the filter. If it is hmpalert.sys (associated with the HitmanPro anti-malware solution), the callback will get immediately unlinked. Conversely, the callback is preserved if the driver’s name matches an entry in the following list: 
对于每个 _CALLBACK_NODE ,FudModule 都会获取相应的 FLTMGR!_FLT_INSTANCE 和 FLTMGR!_FLT_FILTER 结构体,并使用它们来决定是否取消链接回调节点。第一个检查基于过滤器背后的驱动程序的名称。如果是 hmpalert.sys (与HitmanPro反恶意软件解决方案相关),回调将立即取消链接。相反,如果驱动程序的名称与以下列表中的条目匹配,则保留回调:

bindflt.sys  storqosflt.sys  wcifs.sys  cldflt.sys 
filecrypt.sys  luafv.sys  npsvctrig.sys  wof.sys 
fileinfo.sys  applockerfltr.sys  bfs.sys 
Kernel modules that are allowlisted to preserve their file system minifilters.
列入白名单以保留其文件系统微过滤器的内核模块。

If there was no driver name match, FudModule uses _FLT_FILTER.DefaultAltitude to make its ultimate decision. Callbacks are unlinked if the default altitude belongs either to the range [320000, 329999] (defined as FSFilter Anti-Virus by Microsoft) or the range [360000, 389999] (FSFilter Activity Monitor). Besides unlinking the callback nodes, FudModule also wipes the whole _FLT_INSTANCE.CallbackNodes array in the corresponding _FLT_INSTANCE structures. 
如果没有驱动程序名称匹配,FudModule 使用 _FLT_FILTER.DefaultAltitude 做出最终决定。如果默认海拔属于范围 [320000, 329999] (由 Microsoft 定义为 FSFilter Anti-Virus )或范围 [360000, 389999] ( FSFilter Activity Monitor )。除了取消链接回调节点之外,FudModule 还会擦除相应 _FLT_INSTANCE 结构中的整个 _FLT_INSTANCE.CallbackNodes 数组。

0x10 – Windows Filtering Platform 
0x10 – Windows 过滤平台

Windows Filtering Platform (WFP) is a documented set of APIs designed for host-based network traffic filtering. The WFP API offers capabilities for deep packet inspection as well as for modification or dropping of packets at various layers of the network stack. This is very useful functionality, so it serves as a foundation for a lot of Windows network security software, including intrusion detection/prevention systems, firewalls, and network monitoring tools. The WFP API is accessible both in user and kernel space, with the kernel part offering more powerful functionality. Specifically, the kernel API allows for installing so-called callout drivers, which can essentially hook into the network stack and perform arbitrary actions on the processed network traffic. FudModule is trying to interfere with the installed callout routines in an attempt to disrupt the security they provide.  
Windows 过滤平台 (WFP) 是一组记录在案的 API,专为基于主机的网络流量过滤而设计。 WFP API 提供深度数据包检查以及在网络堆栈的各个层修改或丢弃数据包的功能。这是非常有用的功能,因此它是许多 Windows 网络安全软件的基础,包括入侵检测/预防系统、防火墙和网络监控工具。 WFP API 可在用户空间和内核空间中访问,其中内核部分提供更强大的功能。具体来说,内核 API 允许安装所谓的标注驱动程序,该驱动程序本质上可以挂接到网络堆栈并对已处理的网络流量执行任意操作。 FudModule 试图干扰已安装的标注例程,以破坏它们提供的安全性。

This rootkit technique is executed only when Kaspersky drivers (klam.sysklif.sysklwfp.sysklwtp.sysklboot.sys) are present on the targeted system and at the same time Symantec/Broadcom drivers (symevnt.sysbhdrvx64.syssrtsp64.sys) are absent. This check appears to be a new addition in the current version of FudModule. In other aspects, our analysis revealed that the core idea of this technique matches the findings described by ESET researchers during their analysis of the previous version. 
此 Rootkit 技术仅在 Kaspersky 驱动程序( klam.sys 、 klif.sys 、 klwfp.sys 、 klwtp.sys 、 klboot.sys )时执行目标系统上存在 Symantec/Broadcom 驱动程序( symevnt.sys 、 bhdrvx64.sys 、 srtsp64.sys )。此检查似乎是 FudModule 当前版本中的新增内容。在其他方面,我们的分析表明该技术的核心思想与 ESET 研究人员在分析先前版本时描述的发现相匹配。

Initially, FudModule resolves netio!WfpProcessFlowDelete to locate the address of netio!gWfpGlobal. As the name suggests, this is designed to store WFP-related global variables. Although its exact layout is undocumented, it is not hard to find the build-specific offset where a pointer to an array of WFP callout structures is stored (with the length of this array stored at an offset immediately preceding the pointer). FudModule follows this pointer and iterates over the array, skipping all callouts implemented in ndu.systcpip.sysmpsdrv.sys, or wtd.sys. For the remaining callouts, FudModule accesses the callout structure’s flags and sets the flag stored in the least significant bit. While the callout structure itself is undocumented, this particular 0x01 flag is documented in another structure, where it is called FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW. The documentation reads “if this flag is specified, the filter engine calls the callout driver’s classifyFn2 callout function only if there is a context associated with the data flow”. In other words, setting this flag will conditionally disable the callout in cases where no flow context is available (see the implementation of netio!IsActiveCallout below). 
最初,FudModule 解析 netio!WfpProcessFlowDelete 来定位 netio!gWfpGlobal 的地址。顾名思义,这是为了存储与粮食计划署相关的全局变量而设计的。尽管其确切布局没有记录,但不难找到存储特定于构建的偏移量,其中存储了指向 WFP 标注结构数组的指针(该数组的长度存储在紧邻指针之前的偏移量处)。 FudModule 跟随此指针并迭代数组,跳过 ndu.sys 、 tcpip.sys 、 mpsdrv.sys 或 wtd.sys 中实现的所有标注。对于其余的标注,FudModule 访问标注结构的标志并设置存储在最低有效位中的标志。虽然标注结构本身没有记录,但这个特定的 0x01 标志记录在另一个结构中,称为 FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW 。文档中写道“如果指定了此标志,则仅当存在与数据流关联的上下文时,过滤器引擎才会调用标注驱动程序的classifyFn2标注函数”。换句话说,设置此标志将在没有可用的流上下文的情况下有条件地禁用标注(请参阅下面的 netio!IsActiveCallout 的实现)。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
The meaning of the FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW flag can be nicely seen in netio!IsActiveCallout. If this flag is set and no flow context can be obtained, IsActiveCallout will return false (see the highlighted part of the condition). 
FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW 标志的含义可以在 netio!IsActiveCallout 中很好地看出。如果设置了此标志并且无法获取流上下文,则 IsActiveCallout 将返回 false (请参阅条件的突出显示部分)。

While this rootkit technique has the potential to interfere with some WFP callouts, it will not be powerful enough to disrupt all of them. Many WFP callouts registered by security vendors already have the FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW flag set by design, so they will not be affected by this technique at all. Given the initial driver check, it seems like this technique might be targeted directly at Kaspersky. While Kaspersky does install dozens of WFP callouts, about half of those are designed for processing flows and already have the FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW flag set. Since we refrained from reverse engineering our competitor’s products, the actual impact of this rootkit technique remains unclear. 
虽然这种 Rootkit 技术有可能干扰世界粮食计划署的某些标注,但其威力不足以破坏所有标注。安全供应商注册的许多 WFP 标注已按设计设置了 FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW 标志,因此它们根本不会受到此技术的影响。鉴于最初的驱动程序检查,该技术似乎可能直接针对卡巴斯基。虽然卡巴斯基确实安装了数十个 WFP 标注,但其中大约一半是为处理流程设计的,并且已经设置了 FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW 标志。由于我们没有对竞争对手的产品进行逆向工程,因此这种 Rootkit 技术的实际影响仍不清楚。

0x20 – Missing  0x20 – 失踪

So far, the rootkit techniques we analyzed were similar to those detailed by ESET in their paper on the earlier rootkit variant. But starting from now, we are getting into a whole new territory. The 0x20 technique, which used to deal with Event Tracing for Windows (ETW), has been deprecated, leaving the 0x20 bit unused. Instead, there are two new replacement techniques that target ETW, indexed with the bits 0x40 and 0x80. The indexing used to end at 0x40, which was a technique to obstruct forensic analysis by disabling prefetch file creation. However, now the bits go all the way up to 0x200, with two additional new techniques that we will delve into later in this blog. 
到目前为止,我们分析的 Rootkit 技术与 ESET 在其有关早期 Rootkit 变体的论文中详细介绍的技术类似。但从现在开始,我们正在进入一个全新的领域。用于处理 Windows 事件跟踪 (ETW) 的 0x20 技术已被弃用,而 0x20 位未被使用。相反,有两种针对 ETW 的新替换技术,使用位 0x40 和 0x80 进行索引。索引通常以 0x40 结束,这是一种通过禁用预取文件创建来阻碍取证分析的技术。然而,现在这些位一直到 0x200 ,还有另外两种新技术,我们将在本博客后面深入研究。

0x40 – Event Tracing for Windows: System Loggers
0x40 – Windows 事件跟踪:系统记录器

Event Tracing for Windows (ETW) serves as a high-performance mechanism dedicated to tracing and logging events. In a nutshell, its main purpose is to connect providers (who generate some log events) with consumers (who process the generated events). Consumers can define which events they would like to consume, for instance, by selecting some specific providers of interest. There are providers built into the operating system, like Microsoft-Windows-Kernel-Process which generates process-related events, such as process creation or termination. However, third-party applications can also define their custom providers.  
Windows 事件跟踪 (ETW) 是一种专用于跟踪和记录事件的高性能机制。简而言之,它的主要目的是将提供者(生成一些日志事件)与消费者(处理生成的事件)连接起来。例如,消费者可以通过选择一些特定的感兴趣的提供者来定义他们想要消费哪些事件。操作系统中内置了一些提供程序,例如 Microsoft-Windows-Kernel-Process ,它生成与进程相关的事件,例如进程创建或终止。但是,第三方应用程序也可以定义其自定义提供程序。

While many built-in providers are not security-related, some generate events useful for detection purposes. For instance, the Microsoft-Windows-Threat-Intelligence provider makes it possible to watch for suspicious events, such as writing another process’ memory. Furthermore, various security products take advantage of ETW by defining their custom providers and consumers. FudModule tampers with ETW internals in an attempt to intercept suspicious events and thus evade detection. 
虽然许多内置提供程序与安全无关,但有些提供程序会生成可用于检测目的的事件。例如, Microsoft-Windows-Threat-Intelligence 提供程序可以监视可疑事件,例如写入另一个进程的内存。此外,各种安全产品通过定义其自定义提供者和消费者来利用 ETW。 FudModule 篡改 ETW 内部结构,试图拦截可疑事件,从而逃避检测。

The main idea behind this rootkit technique is to disable system loggers by zeroing out EtwpActiveSystemLoggers. The specific implementation of how this address is found varies based on the target Windows version. On newer builds, the nt!EtwSendTraceBuffer routine is resolved first and used to find nt!EtwpHostSiloState. This points to an _ETW_SILODRIVERSTATE structure, and using a hardcoded build-specific offset, the rootkit can access _ETW_SILODRIVERSTATE.SystemLoggerSettings.EtwpActiveSystemLoggers. On older builds, the rootkit first scans the entire ntoskrnl .text section, searching for opcode bytes specific to the EtwTraceKernelEvent prologue. The rootkit then extracts the target address from the mov ebx, cs:EtwpActiveSystemLoggers instruction that immediately follows. 
此 Rootkit 技术背后的主要思想是通过清零 EtwpActiveSystemLoggers 来禁用系统记录器。如何找到此地址的具体实现因目标 Windows 版本而异。在较新的版本中,首先解析 nt!EtwSendTraceBuffer 例程并用于查找 nt!EtwpHostSiloState 。这指向 _ETW_SILODRIVERSTATE 结构,并且使用硬编码的特定于构建的偏移量,rootkit 可以访问 _ETW_SILODRIVERSTATE.SystemLoggerSettings.EtwpActiveSystemLoggers 。在较旧的版本中,rootkit 首先扫描整个 ntoskrnl .text 部分,搜索特定于 EtwTraceKernelEvent 序言的操作码字节。然后,rootkit 从紧随其后的 mov ebx, cs:EtwpActiveSystemLoggers 指令中提取目标地址。

To understand the technique’s impact, we can take a look at how EtwpActiveSystemLoggers is used in the kernel. Accessed on a bit-by-bit basis, its least significant eight bits might be set in the EtwpStartLogger routine. This indicates that the value itself is a bit field, with each bit signifying whether a particular system logger is active. Looking at the other references to EtwpActiveSystemLoggers, a clear pattern emerges. After its value is read, there tends to be a loop guarded by a bsf instruction (bit scan forward). Inside the loop tends to be a call to an ETW-related routine that might generate a log event. The purpose of this loop is to iterate over the set bits of EtwpActiveSystemLoggers. When the rootkit clears all the bits, the body of the loop will never get executed, meaning the event will not get logged. 
为了了解该技术的影响,我们可以看一下 EtwpActiveSystemLoggers 在内核中的使用方式。在逐位访问的基础上,其最低有效的八位可以在 EtwpStartLogger 例程中设置。这表明该值本身是一个位字段,每个位表示特定的系统记录器是否处于活动状态。查看对 EtwpActiveSystemLoggers 的其他引用,出现了一个清晰的模式。读取其值后,往往会出现一个由 bsf 指令(向前扫描位)保护的循环。循环内部往往是对可能生成日志事件的 ETW 相关例程的调用。此循环的目的是迭代 EtwpActiveSystemLoggers 的设置位。当 Rootkit 清除所有位时,循环体将永远不会被执行,这意味着事件不会被记录。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
Example decompilation of EtwpTraceKernelEventWithFilter. After the rootkit zeroes out EtwpActiveSystemLoggersEtwpLogKernelEvent will never get called from inside the loop since the condition guarding the loop will always evaluate to zero. 

0x80 – Event Tracing for Windows: Provider GUIDs 
0x80 – Windows 事件跟踪:提供商 GUID

Complementing the previous technique, the 0x80 technique is also designed to blind ETW, however using a different approach. While the 0x40 technique was quite generic – aiming to disable all system loggers – this technique operates in a more surgical fashion. It contains a hardcoded list of 95 GUIDs, each representing an identifier for some specific ETW provider. The rootkit iterates over all these GUIDs and attempts to disable the respective providers. While this approach requires the attackers to invest some effort into assembling the list of GUIDs, it also offers them a finer degree of control over which ETW providers they will eventually disrupt. This allows them to selectively target providers that pose a higher detection risk and ignore the rest to minimize the rootkit’s impact on the target system. 
作为对先前技术的补充, 0x80 技术也被设计为盲 ETW,但使用了不同的方法。虽然 0x40 技术非常通用——旨在禁用所有系统记录器——但这种技术的运作方式更加外科手术式。它包含 95 个 GUID 的硬编码列表,每个 GUID 代表某个特定 ETW 提供商的标识符。 Rootkit 会迭代所有这些 GUID,并尝试禁用相应的提供程序。虽然这种方法要求攻击者投入一些精力来组装 GUID 列表,但它也使他们能够更好地控制他们最终将破坏哪些 ETW 提供商。这使得他们能够有选择地瞄准那些具有较高检测风险的提供商,并忽略其余的提供商,以最大程度地减少 Rootkit 对目标系统的影响。

This technique starts by obtaining the address of EtwpHostSiloState (or EtwSiloState on older builds). If EtwpHostSiloState was already resolved during the previous technique, the rootkit just reuses the address. If not, the rootkit follows the reference chain PsGetCurrentServerSiloName -> PsGetCurrentServerSiloGlobals -> PspHostSiloGlobals -> EtwSiloState. In both scenarios, the result is that the rootkit just obtained a pointer to an _ETW_SILODRIVERSTATE structure, which contains a member named EtwpGuidHashTable. As the name suggests, this is a hash table holding ETW GUIDs (_ETW_GUID_ENTRY).  
此技术首先获取 EtwpHostSiloState (或旧版本上的 EtwSiloState )的地址。如果 EtwpHostSiloState 在之前的技术中已经被解析,rootkit 只会重用该地址。如果没有,rootkit 遵循参考链 PsGetCurrentServerSiloName -> PsGetCurrentServerSiloGlobals -> PspHostSiloGlobals -> EtwSiloState 。在这两种情况下,结果都是 rootkit 刚刚获得了一个指向 _ETW_SILODRIVERSTATE 结构的指针,其中包含一个名为 EtwpGuidHashTable 的成员。顾名思义,这是一个保存 ETW GUID ( _ETW_GUID_ENTRY ) 的哈希表。

FudModule then iterates over its hardcoded list of GUIDs and attempts to locate each of them in the hash table. Although the hash table internals are officially undocumented, Yarden Shafir provided a nice description in her blog on exploiting an ETW vulnerability. In a nutshell, the hash is computed by just splitting the 128-bit GUID into four 32-bit parts and XORing them together. By ANDing the hash with 0x3F, an index of the relevant hash bucket (_ETW_HASH_BUCKET) can be obtained. The bucket contains three linked lists of _ETW_GUID_ENTRY structures, each designated for a different type of GUIDs. FudModule always opts for the first one (EtwTraceGuidType) and traverses it, looking for the relevant _ETW_GUID_ENTRY structure. 
然后,FudModule 迭代其硬编码的 GUID 列表,并尝试在哈希表中找到每个 GUID。尽管哈希表的内部结构没有正式记录,但 Yarden Shafir 在她的博客中提供了有关利用 ETW 漏洞的精彩描述。简而言之,哈希值的计算方法是将 128 位 GUID 分成四个 32 位部分并将它们异或在一起。通过将哈希值与 0x3F 进行与运算,可以获得相关哈希桶 ( _ETW_HASH_BUCKET ) 的索引。该存储桶包含三个 _ETW_GUID_ENTRY 结构的链表,每个链表指定不同类型的 GUID。 FudModule 始终选择第一个 ( EtwTraceGuidType ) 并遍历它,寻找相关的 _ETW_GUID_ENTRY 结构。

With a pointer to _ETW_GUID_ENTRY corresponding to a GUID of interest, FudModule proceeds to clear _ETW_GUID_ENTRY.ProviderEnableInfo.IsEnabled. The purpose of this modification seems self-explanatory: FudModule is trying to disable the ETW provider. To better understand how this works, let’s examine nt!EtwEventEnabled (see the decompiled code below). This is a routine that often serves as an if condition before nt!EtwWrite (or nt!EtwWriteEx) gets called.  
通过指向与感兴趣的 GUID 相对应的 _ETW_GUID_ENTRY 的指针,FudModule 继续清除 _ETW_GUID_ENTRY.ProviderEnableInfo.IsEnabled 。此修改的目的似乎不言自明:FudModule 正在尝试禁用 ETW 提供程序。为了更好地理解它是如何工作的,让我们检查一下 nt!EtwEventEnabled (请参阅下面的反编译代码)。这是一个例程,通常在调用 nt!EtwWrite (或 nt!EtwWriteEx )之前充当 if 条件。

Looking at the decompilation, there are two return 1 statements. Setting ProviderEnableInfo.IsEnabled to zero ensures that the first one is never reached. However, the second return statement could still potentially execute. To make sure this doesn’t happen, the rootkit also iterates over all _ETW_REG_ENTRY structures from the _ETW_GUID_ENTRY.RegListHead linked list. For each of them, it makes a single doubleword write to zero out four masks, namely EnableMaskGroupEnableMaskHostEnableMask, and HostGroupEnableMask (or only EnableMask and GroupEnableMask on older builds, where the latter two masks were not yet introduced).  
看反编译,有两条 return 1 语句。将 ProviderEnableInfo.IsEnabled 设置为零可确保永远不会到达第一个。但是,第二个 return 语句仍然可能执行。为了确保这种情况不会发生,rootkit 还会迭代 _ETW_GUID_ENTRY.RegListHead 链表中的所有 _ETW_REG_ENTRY 结构。对于它们中的每一个,它都会进行单个双字写入以将四个掩码清零,即 EnableMask 、 GroupEnableMask 、 HostEnableMask 和 HostGroupEnableMask (或者仅在旧版本上使用 EnableMask 和 GroupEnableMask ,其中后两个掩码尚未引入)。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
Decompilation of nt!EtwEventEnabled. After the rootkit has finished its job, this routine will always return false for events related to the targeted GUIDs. This is because the rootkit cleared both _ETW_GUID_ENTRY.ProviderEnableInfo.IsEnabled and _ETW_REG_ENTRY.GroupEnableMask, forcing the highlighted conditions to fail. 
nt!EtwEventEnabled 的反编译。 Rootkit 完成其工作后,此例程将始终返回与目标 GUID 相关的事件 false 。这是因为 rootkit 清除了 _ETW_GUID_ENTRY.ProviderEnableInfo.IsEnabled 和 _ETW_REG_ENTRY.GroupEnableMask ,迫使突出显示的条件失败。

Clearing these masks also has an additional effect beyond making EtwEventEnabled always return false. These four are all also checked in EtwWriteEx and this modification effectively neutralizes this routine, as when no mask is set for a particular event registration object, execution will never proceed to a lower-level routine (nt!EtwpEventWriteFull) where the bulk of the actual event writing logic is implemented. 
除了使 EtwEventEnabled 始终返回 false 之外,清除这些掩码还具有额外的效果。这四个也都在 EtwWriteEx 中进行检查,并且此修改有效地中和了此例程,因为当没有为特定事件注册对象设置掩码时,执行将永远不会继续到较低级别的例程( nt!EtwpEventWriteFull

0x100 – Image Verification Callbacks 
0x100 – 图像验证回调

Image verification callbacks are yet another callback mechanism disrupted by FudModule. Designed similarly to process/thread/image callbacks, image verification callbacks are supposed to get invoked whenever a new driver image is loaded into kernel memory. This represents useful functionality for anti-malware software, which can leverage them to blocklist known malicious or vulnerable drivers (though there might be some problems with this blocking approach as the callbacks get invoked asynchronously). Furthermore, image verification callbacks also offer a valuable source of telemetry, providing visibility into suspicious driver load events. The callbacks can be registered using the SeRegisterImageVerificationCallback routine, which is publicly undocumented. As a result of this undocumented nature, the usage here is limited mainly to deep-rooted anti-malware software. For instance, Windows Defender registers a callback named WdFilter!MpImageVerificationCallback
图像验证回调是 FudModule 破坏的另一种回调机制。设计与进程/线程/图像回调类似,每当新的驱动程序图像加载到内核内存时,图像验证回调就会被调用。这代表了反恶意软件软件的有用功能,可以利用它们将已知的恶意或易受攻击的驱动程序列入黑名单(尽管这种阻止方法可能会出现一些问题,因为回调会被异步调用)。此外,图像验证回调还提供了宝贵的遥测来源,提供对可疑驱动程序加载事件的可见性。可以使用 SeRegisterImageVerificationCallback 例程注册回调,该例程未公开记录。由于这种未记录的性质,此处的使用主要限于根深蒂固的反恶意软件。例如,Windows Defender 注册一个名为 WdFilter!MpImageVerificationCallback 的回调。

As the kernel internally manages image verification callbacks in a similar fashion to some of the other callbacks we already explored, the rootkit’s removal implementation will undoubtedly seem familiar. First, the rootkit resolves the nt!SeRegisterImageVerificationCallback routine and scans its body to locate nt!ExCbSeImageVerificationDriverInfo. Dereferencing this, it obtains a pointer to a _CALLBACK_OBJECT structure, which holds the callbacks in the _CALLBACK_OBJECT.RegisteredCallbacks linked list. This list consists of _CALLBACK_REGISTRATION structures, where the actual callback function pointer can be found in _CALLBACK_REGISTRATION.CallbackFunction. FudModule clears the entire list by making the RegisteredCallbacks head LIST_ENTRY point directly to itself. Additionally, it also walks the original linked list and similarly short-circuits each individual _CALLBACK_REGISTRATION entry in the list. 
由于内核在内部管理图像验证回调的方式与我们已经探索过的其他一些回调类似,因此 rootkit 的删除实现无疑看起来很熟悉。首先,rootkit 解析 nt!SeRegisterImageVerificationCallback 例程并扫描其主体以定位 nt!ExCbSeImageVerificationDriverInfo 。取消引用它,它获得一个指向 _CALLBACK_OBJECT 结构的指针,该结构在 _CALLBACK_OBJECT.RegisteredCallbacks 链接列表中保存回调。该列表由 _CALLBACK_REGISTRATION 结构组成,实际的回调函数指针可以在 _CALLBACK_REGISTRATION.CallbackFunction 中找到。 FudModule 通过使 RegisteredCallbacks 头 LIST_ENTRY 直接指向自身来清除整个列表。此外,它还会遍历原始链接列表,并类似地短路列表中的每个单独的 _CALLBACK_REGISTRATION 条目。

This rootkit technique is newly implemented in the current version of FudModule, and we can only speculate on the motivation here. It seems to be designed to help avoid detection when loading either a vulnerable or a malicious driver. However, it might be hard to understand why Lazarus should want to load an additional driver if they already have control over the kernel. It would make little sense for them to load a vulnerable driver, as they already established their kernel read/write primitive by exploiting a zero-day in a preinstalled Windows driver. Further, even if they were exploiting a vulnerable driver in the first place (as was the case in the previous version of FudModule), it would be simply too late to unlink the callback now. By the time this rootkit technique executes, the image verification callback for the vulnerable driver would have already been invoked. Therefore, we believe the most likely explanation is that the threat actors are preparing the grounds for loading some malicious driver later. Perhaps the idea is that they just want to be covered in case they decide to deploy some additional kernel-mode payload in the future. 
该rootkit技术是在当前版本的FudModule中新实现的,我们只能推测其动机。它的设计似乎是为了在加载易受攻击或恶意的驱动程序时帮助避免检测。然而,如果 Lazarus 已经控制了内核,那么可能很难理解为什么他们应该加载额外的驱动程序。对于他们来说,加载易受攻击的驱动程序没有什么意义,因为他们已经通过利用预装 Windows 驱动程序中的零日漏洞建立了内核读/写原语。此外,即使他们一开始就利用了易受攻击的驱动程序(就像以前版本的 FudModule 中的情况一样),现在取消回调链接也为时已晚。当此 Rootkit 技术执行时,易受攻击的驱动程序的图像验证回调可能已经被调用。因此,我们认为最可能的解释是,威胁行为者正在为稍后加载一些恶意驱动程序做准备。也许他们的想法是,他们只是想在将来决定部署一些额外的内核模式有效负载时被覆盖。

0x200 – Direct Attacks on Security Software 
0x200 – 对安全软件的直接攻击

The rootkit techniques we explored up to this point were all somewhat generic. Each targeted some security-related system component and, through it, indirectly interfered with all security software that relied on the component. In contrast, this final technique goes straight to the point and aims to directly disable specific security software. In particular, the targeted security solutions are AhnLab V3 Endpoint Security, Windows Defender, CrowdStrike Falcon, and HitmanPro. 
到目前为止,我们探索的 Rootkit 技术都有些通用。每个攻击都针对一些与安全相关的系统组件,并通过它间接干扰依赖该组件的所有安全软件。相比之下,最后一项技术开门见山,旨在直接禁用特定的安全软件。特别是,目标安全解决方案是 AhnLab V3 Endpoint Security、Windows Defender、CrowdStrike Falcon 和 HitmanPro。

The attack starts with the rootkit obtaining the address of its own _EPROCESS structure. This is done using NtDuplicateHandle to duplicate the current process pseudohandle and then calling NtQuerySystemInformation to get SystemExtendedHandleInformation. With the extended handle information, the rootkit looks for an entry corresponding to the duplicated handle and obtains the _EPROCESS pointer from there. Using NtQuerySystemInformation to leak kernel pointers is a well-known technique that Microsoft aims to restrict by gradually building up mitigations. However, attackers capable of enabling SeDebugPrivilege at high integrity levels are out of scope of these mitigations, so FudModule can keep using this technique, even on the upcoming 24H2 builds. With the _EPROCESS pointer, FudModule disables mitigations by zeroing out _EPROCESS.MitigationFlags. Then, it also clears the EnableHandleExceptions flag from _EPROCESS.ObjectTable.Flags. We believe this is meant to increase stability in case something goes wrong later during the handle table entry manipulation technique that we will describe shortly.  
攻击从 rootkit 获取其自己的 _EPROCESS 结构的地址开始。这是通过使用 NtDuplicateHandle 复制当前进程伪句柄,然后调用 NtQuerySystemInformation 获取 SystemExtendedHandleInformation 来完成的。利用扩展句柄信息,rootkit 查找与重复句柄相对应的条目,并从那里获取 _EPROCESS 指针。使用 NtQuerySystemInformation 泄漏内核指针是一种众所周知的技术,微软旨在通过逐步建立缓解措施来限制这种技术。然而,能够以高完整性级别启用 SeDebugPrivilege 的攻击者超出了这些缓解措施的范围,因此 FudModule 可以继续使用此技术,即使在即将到来的 24H2 版本中也是如此。使用 _EPROCESS 指针,FudModule 通过将 _EPROCESS.MitigationFlags 清零来禁用缓解措施。然后,它还会清除 _EPROCESS.ObjectTable.Flags 中的 EnableHandleExceptions 标志。我们相信这是为了提高稳定性,以防稍后在我们将很快描述的句柄表条目操作技术期间出现问题。

Regarding the specific technique used to attack the security solutions, AhnLab is handled differently than the other three targets. FudModule first checks if AhnLab is even running, by traversing the ActiveProcessLinks linked list and looking for a process named asdsvc.exe (AhnLab Smart Defense Service) with _EPROCESS.Token.AuthenticationId set to SYSTEM_LUID. If such a process is found, FudModule clears its _EPROCESS.Protection byte, effectively toggling off PPL protection for the process. While this asdsvc.exe process is under usual circumstances meant to be protected at the standard PsProtectedSignerAntimalware level, this modification makes it just a regular non-protected process. This opens it up to further attacks from user mode, where now even other privileged, yet non-protected processes could be able to tamper with it. However, we suspect the main idea behind this technique might be to disrupt the link between AhnLab’s user-mode and kernel-mode components. By removing the service’s PPL protection, the kernel-mode component might no longer recognize it as a legitimate AhnLab component. However, this is just a speculation as we didn’t test the real impact of this technique. 
关于攻击安全解决方案的具体技术,AhnLab 的处理方式与其他三个目标不同。 FudModule 首先检查 AhnLab 是否正在运行,方法是遍历 ActiveProcessLinks 链接列表并查找名为 asdsvc.exe (AhnLab 智能防御服务)的进程,其中 _EPROCESS.Token.AuthenticationId 设置为 SYSTEM_LUID 。如果找到这样的进程,FudModule 会清除其 _EPROCESS.Protection 字节,从而有效地关闭该进程的 PPL 保护。虽然此 asdsvc.exe 进程在通常情况下应在标准 PsProtectedSignerAntimalware 级别受到保护,但此修改使其只是一个常规的不受保护进程。这使得它容易受到来自用户模式的进一步攻击,现在甚至其他特权但未受保护的进程也可以对其进行篡改。然而,我们怀疑这种技术背后的主要思想可能是破坏 AhnLab 用户模式和内核模式组件之间的链接。通过删除服务的 PPL 保护,内核模式组件可能不再将其识别为合法的 AhnLab 组件。然而,这只是一种猜测,因为我们没有测试该技术的真正影响。

Handle Table Entry Manipulation
处理表条目操作

The technique employed to attack Defender, CrowdStrike, and HitmanPro is much more intriguing: FudModule attempts to suspend them using a new handle table entry manipulation technique. To better understand this technique, let’s begin with a brief background on handle tables. When user-mode code interacts with kernel objects such as processes, files, or mutexes, it typically doesn’t work with the objects directly. Instead, it references them indirectly through handles. Internally, the kernel must be able to translate the handle to the corresponding object, and this is where the handle table comes in. This per-process table, available at _EPROCESS.ObjectTable.TableCode, serves as a mapping from handles to the underlying objects. Organized as an array, it is indexed by the integer value of the handle. Each element is of type _HANDLE_TABLE_ENTRY and contains two crucial pieces of information: a (compressed) pointer to the object’s header (nt!_OBJECT_HEADER) and access bits associated with the handle. 
用于攻击 Defender、CrowdStrike 和 HitmanPro 的技术更加有趣:FudModule 尝试使用新的句柄表条目操作技术来暂停它们。为了更好地理解这项技术,让我们首先了解句柄表的简要背景。当用户模式代码与进程、文件或互斥体等内核对象交互时,它通常不直接与这些对象一起工作。相反,它通过句柄间接引用它们。在内部,内核必须能够将句柄转换为相应的对象,这就是句柄表的用武之地。这个每进程表位于 _EPROCESS.ObjectTable.TableCode 处,用作从句柄到对象的映射。底层对象。它被组织为一个数组,通过句柄的整数值进行索引。每个元素都是 _HANDLE_TABLE_ENTRY 类型,并包含两个关键信息:指向对象标头 ( nt!_OBJECT_HEADER ) 的(压缩)指针和与句柄关联的访问位。

Due to this handle design, kernel object access checks are typically split into two separate logical steps. The first step happens when a process attempts to acquire a handle (such as opening a file with CreateFile). During this step, the current thread’s token is typically checked against the target object’s security descriptor to ensure that the thread is allowed to obtain a handle with the desired access mask. The second check takes place when a process performs an operation using an already acquired handle (such as writing to a file with WriteFile). This typically only involves verifying that the handle is powerful enough (meaning it has the right access bits) for the requested operation.  
由于这种句柄设计,内核对象访问检查通常分为两个单独的逻辑步骤。第一步发生在进程尝试获取句柄时(例如使用 CreateFile 打开文件)。在此步骤中,通常会根据目标对象的安全描述符检查当前线程的令牌,以确保允许线程获取具有所需访问掩码的句柄。当进程使用已获取的句柄执行操作(例如使用 WriteFile 写入文件)时,会进行第二次检查。这通常只涉及验证句柄对于所请求的操作是否足够强大(意味着它具有正确的访问位)。

FudModule executes as a non-protected process, so it theoretically shouldn’t be able to obtain a powerful handle to a PPL-protected process such as the CrowdStrike Falcon Service. However, leveraging the kernel read/write primitive, FudModule has the ability to access the handle table directly. This allows it to craft a custom handle table entry with control over both the referenced object and the access bits. This way, it can conjure an arbitrary handle to any object, completely bypassing the check typically needed for handle acquisition. What’s more, if it sets the handle’s access bits appropriately, it will also satisfy the subsequent handle checks when performing its desired operations. 
FudModule 作为不受保护的进程执行,因此理论上它不应该能够获得受 PPL 保护的进程(例如 CrowdStrike Falcon Service)的强大句柄。然而,利用内核读/写原语,FudModule 能够直接访问句柄表。这允许它制作一个自定义句柄表条目,并控制引用的对象和访问位。这样,它可以为任何对象生成任意句柄,完全绕过句柄获取通常所需的检查。更重要的是,如果它适当地设置句柄的访问位,它在执行所需操作时也将满足后续的句柄检查。

To prepare for the handle table entry manipulation technique, FudModule creates a dummy thread that just puts itself to sleep immediately. The thread itself is not important. What is important is that by calling CreateThread, the rootkit just obtained a thread handle with THREAD_ALL_ACCESS rights. This handle is the one that will have its handle table entry manipulated. Since it already has very powerful access bits, the rootkit will not even have to touch its _HANDLE_TABLE_ENTRY.GrantedAccessBits. All it needs to do is overwrite _HANDLE_TABLE_ENTRY.ObjectPointerBits to redirect the handle to an arbitrary object of its choice. This will make the handle reference that object and enable the rootkit to perform privileged operations on it. Note that ObjectPointerBits is not the whole pointer to the object: it only represents 44 bits of the 64-bit pointer. But since the _OBJECT_HEADER pointed to by ObjectPointerBits is guaranteed to be aligned (meaning the least significant four bits must be zero) and in kernel address space (meaning the most significant sixteen bits must be 0xFFFF), the remaining 20 bits can be easily inferred. 
为了准备句柄表条目操作技术,FudModule 创建一个虚拟线程,该线程立即将其自身置于睡眠状态。线程本身并不重要。重要的是,通过调用 CreateThread ,rootkit 刚刚获得了具有 THREAD_ALL_ACCESS 权限的线程句柄。该句柄将对其句柄表条目进行操作。由于它已经具有非常强大的访问位,rootkit 甚至不必触及它的 _HANDLE_TABLE_ENTRY.GrantedAccessBits 。它所需要做的就是覆盖 _HANDLE_TABLE_ENTRY.ObjectPointerBits 以将句柄重定向到它选择的任意对象。这将使句柄引用该对象并使 rootkit 能够对其执行特权操作。请注意, ObjectPointerBits 并不是指向对象的整个指针:它仅表示 64 位指针的 44 位。但由于 ObjectPointerBits 指向的 _OBJECT_HEADER 保证是对齐的(意味着最低有效四位必须为零)并且在内核地址空间中(意味着最高有效十六位必须是 0xFFFF ),可以轻松推断出剩余的 20 位。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
A dummy thread whose handle will be the subject of handle table entry manipulation. 
一个虚拟线程,其句柄将成为句柄表条目操作的主题。

The specific processes targeted by this technique are MsSense.exeMsMpEng.exeCSFalconService.exe, and hmpalert.exe. FudModule first finds their respective _EPROCESS structures, employing the same algorithm as it did to find the AhnLab service. Then, it performs a sanity check to ensure that the dummy thread handle is not too high by comparing it with _EPROCESS.ObjectTable.NextHandleNeedingPool (which holds information on the maximum possible handle value given the current handle table allocation size). With the sanity check satisfied, FudModule accesses the handle table itself (EPROCESS.ObjectTable.TableCode) and modifies the dummy thread’s _HANDLE_TABLE_ENTRY so that it points to the _OBJECT_HEADER of the target _EPROCESS. Finally, the rootkit uses the redirected handle to call NtSuspendProcess, which will suspend the targeted process.  
此技术针对的特定进程是 MsSense.exe 、 MsMpEng.exe 、 CSFalconService.exe 和 hmpalert.exe 。 FudModule 首先找到它们各自的 _EPROCESS 结构,采用与查找 AhnLab 服务相同的算法。然后,它通过与 _EPROCESS.ObjectTable.NextHandleNeedingPool 进行比较来执行健全性检查,以确保虚拟线程句柄不会太高(它保存有关给定当前句柄表分配大小的最大可能句柄值的信息)。满足完整性检查后,FudModule 访问句柄表本身 ( EPROCESS.ObjectTable.TableCode ) 并修改虚拟线程的 _HANDLE_TABLE_ENTRY ,使其指向目标 _OBJECT_HEADER 。 b9> 。最后,rootkit使用重定向句柄调用 NtSuspendProcess ,这将挂起目标进程。

It might seem odd that the manipulated handle used to be a thread handle, but now it’s being used as a process handle. In practice, there is nothing wrong with this since the handle table itself holds no object type information. The object type is stored in _OBJECT_HEADER.TypeIndex so when the rootkit redirected the handle, it also effectively changed the handle object type. As for the access bits, the original THREAD_ALL_ACCESS gets reinterpreted in the new context as PROCESS_ALL_ACCESS since both constants share the same underlying value. 
被操纵的句柄曾经是线程句柄,但现在它被用作进程句柄,这可能看起来很奇怪。实际上,这没有任何问题,因为句柄表本身不保存对象类型信息。对象类型存储在 _OBJECT_HEADER.TypeIndex 中,因此当 rootkit 重定向句柄时,它也有效地更改了句柄对象类型。至于访问位,原始的 THREAD_ALL_ACCESS 在新上下文中被重新解释为 PROCESS_ALL_ACCESS ,因为两个常量共享相同的基础值。

Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day
The manipulated dummy thread handle (0x168), now referencing a process object. 
受操纵的虚拟线程句柄 ( 0x168 ),现在引用进程对象。

Though suspending the target process might initially appear to be a completed job, FudModule doesn’t stop here. After taking five seconds of sleep, it also attempts to iterate over all the threads in the target process, suspending them one by one. When all threads are suspended, FudModule uses NtResumeProcess to resume the suspended process. At this point, while the process itself is technically resumed, its individual threads remain suspended, meaning the process is still effectively in a suspended state. We can only speculate why Lazarus implemented process suspension this way, but it seems like an attempt to make the technique stealthier. After all, a suspended process is much more conspicuous than just several threads with increased suspend counts. 
尽管暂停目标进程最初看起来似乎是一项已完成的工作,但 FudModule 并没有就此停止。在进行五秒钟的睡眠后,它还会尝试迭代目标进程中的所有线程,将它们一一挂起。当所有线程都挂起时,FudModule 使用 NtResumeProcess 来恢复挂起的进程。此时,虽然进程本身在技术上已恢复,但其各个线程仍保持挂起状态,这意味着进程实际上仍处于挂起状态。我们只能推测为什么 Lazarus 以这种方式实现进程暂停,但这似乎是一种使该技术更加隐蔽的尝试。毕竟,挂起的进程比挂起计数增加的几个线程要显眼得多。

To enumerate threads, FudModule calls NtQuerySystemInformation with the SystemExtendedHandleInformation class. Iterating over the returned handle information, FudModule searches for thread handles from the target process. The owner process is checked by comparing the PID of the target process with SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.UniqueProcessId and the type is checked by comparing SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.ObjectTypeIndex with the thread type index, which was previously obtained using NtQueryObject to get ObjectTypesInformation. For each enumerated thread (which might include some threads multiple times, as there might be more than one open handle to the same thread), FudModule manipulates the dummy thread handle so that it points to the enumerated thread and suspends it by calling SuspendThread on the manipulated handle. Finally, after all threads are suspended and the process resumed, FudModule restores the manipulated handle to its original state, once again referencing the dummy sleep thread. 
为了枚举线程,FudModule 使用 SystemExtendedHandleInformation 类调用 NtQuerySystemInformation 。 FudModule 迭代返回的句柄信息,从目标进程中搜索线程句柄。通过将目标进程的 PID 与 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.UniqueProcessId 进行比较来检查所有者进程,通过将 SYSTEM_HANDLE_TABLE_ENTRY_INFO_EX.ObjectTypeIndex 与之前使用 NtQueryObject 。对于每个枚举线程(可能多次包含某些线程,因为同一线程可能有多个打开的句柄),FudModule 操作虚拟线程句柄,使其指向枚举线程并通过调用 SuspendThread 在被操纵的手柄上。最后,在所有线程被挂起并且进程恢复之后,FudModule 将被操作的句柄恢复到其原始状态,再次引用虚拟睡眠线程。

Conclusion  结论

The Lazarus Group remains among the most prolific and long-standing advanced persistent threat actors. Though their signature tactics and techniques are well-recognized by now, they still occasionally manage to surprise us with an unexpected level of technical sophistication. The FudModule rootkit serves as the latest example, representing one of the most complex tools Lazarus holds in their arsenal. Recent updates examined in this blog show Lazarus’ commitment to keep actively developing this rootkit, focusing on improvements in both stealth and functionality. 
拉撒路集团仍然是最多产且长期存在的高级持续威胁行为者之一。尽管他们标志性的战术和技术现在已广为人知,但他们偶尔仍会以意想不到的技术复杂程度给我们带来惊喜。 FudModule rootkit 是最新的例子,代表了 Lazarus 拥有的最复杂的工具之一。本博客中检查的最新更新表明 Lazarus 致力于继续积极开发此 rootkit,重点关注隐秘性和功能性的改进。

With their admin-to-kernel zero-day now burned, Lazarus is confronted with a significant challenge. They can either discover a new zero-day exploit or revert to their old BYOVD techniques. Regardless of their choice, we will continue closely monitoring their activity, eager to see how they will cope with these new circumstances. 
由于管理到内核的零日漏洞现已被烧毁,Lazarus 面临着重大挑战。他们可以发现新的零日漏洞,也可以恢复到旧的 BYOVD 技术。无论他们如何选择,我们都将继续密切关注他们的活动,急切地想知道他们将如何应对这些新情况。

Indicators of Compromise (IoCs) 
妥协指标 (IoC)

A YARA rule for the latest FudModule variant is available at https://github.com/avast/ioc/tree/master/FudModule#yara.
最新 FudModule 变体的 YARA 规则位于 https://github.com/avast/ioc/tree/master/FudModule#yara。

原文始发于 Jan Vojtěšek:Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day

版权声明:admin 发表于 2024年2月29日 上午9:15。
转载请注明:Lazarus and the FudModule Rootkit: Beyond BYOVD with an Admin-to-Kernel Zero-Day | CTF导航

相关文章