Lazarus&Byovd详细分析-翻译
参考链接:https://www.virusbulletin.com/uploads/pdf/conference/vb2022/VB2022-Kalnai-Havranek.pdf
摘要
根据Microsoft Windows安全服务标准的定义,管理员到内核的转换不是安全边界。
尽管如此,拥有修改内核内存的能力是一个优势,特别是如果攻击者可以从用户空间实现这一点。自带漏洞BYOVD技术是一种可行的方法:攻击者携带并加载具有有效签名的特定内核驱动程序,从而克服了驱动程序签名强制(DSE)。此外,该驱动程序包含一个漏洞,该漏洞会使攻击者获得任意内核写。在这种情况下,Windows API不再是一种限制,对手可以随意篡改操作系统的特权区域。
为了成功完成这项任务,必须经历一个无疑复杂和耗时的过程:
选择一个合适的易受伤害的驱动;
研究Windows的内部,因为内核的功能没有很好的文档记录;
使用大多数开发人员不熟悉的代码库;
最后是测试,因为任何未处理的错误都是BSOD之前的最后一步,这可能会引发后续调查和访问丢失。
在本文中,我们深入分析了Lazarus在2021晚些时候的APT攻击中使用的恶意组件。该恶意软件是一个复杂的、以前未记录的用户模式模块,使用BYOVD技术,并利用合法的、签名的Dell驱动程序中的CVE-2021-21551漏洞。
在获得对内核内存的写访问之后,该模块的全局目标是使安全解决方案和监控工具失效。这是通过七种不同的机制实现的,这些机制针对7.1版到Windows Server 2022版的Windows系统的重要内核函数、结构和变量。我们将通过演示这些机制的操作方式以及一旦执行用户模式模块,它们对系统监控所做的更改,来进一步了解这些机制。
与使用BYOVD的其他APT相比,这个Lazarus案例是独一无二的,因为它拥有一系列复杂的方法来禁用监控接口,这些方法在野外从未见过。虽然一些单独的技术可能已经被漏洞研究人员和游戏作弊者发现,但我们将对所有这些技术进行全面分析,并将其置于上下文中。
引言
2021年10月,我们记录了一次对荷兰一家公司网络端点的攻击[1]。受害者的计算机上部署了各种类型的恶意工具,其中许多工具可以肯定地归因于Lazarus 。除了HTTP(S)后门、下载器和上传器等常见恶意软件外,还有一个例子吸引了我们的好奇心——一个内部名为FudModule的88064字节用户模式动态链接库。其功能是本文的主要主题。
FUDMODULE
安装
FudModule的整个交付链未完全复现。
最初的发现是shell代码,其中一个加密缓冲区运行在一个合法但被破坏的msiexec.exe的内存空间中。在图1中,可以看到加载解密缓冲区(1_ au8Decrypted)的动作,其中包含FudModule,并且它导出的Close函数的64位返回值(ret_Close)以十六进制字符串的形式存储在C:\WINDOWS\WINDOWS.ini中。返回值表示有效载荷在其任务中的成功程度。
事实证明,FudModule的功能主要集中在Windows内核空间。
但是,用户模式DLL不能直接读取或写入内核内存。为此,该模块利用“自带漏洞驱动程序”(BYOVD)技术,加载由Dell开发的嵌入式、有效签名的合法驱动程序DBUtil_2_3.sys。
驱动程序中存在各种缺陷,2021年5月分配了一个CVE:CVE-2021-21551。
攻击者只对获取内核写入原语感兴趣。如果此步骤失败,模块退出,因为任何进一步的操作都不可能完成。
驱动程序被放入:\WINDOWS\System32\drivers\文件夹中,其名称从circlassmgr.sys、dmvscmgr.sys,hidirmgr.sys,isapnpmgr.sys,mspqmmgr.sys和umpassmgr.sys中随机选择。请注意,此操作已经需要管理员权限。
在图2中,CVE-2021-21551是通过使用特定控制代码调用API触发的,并且缓冲器代码(IOCTL_VIRTUAL_WRITE)是驱动程序为内核写入漏洞执行正确的DBUtil_2_3程序所需的值。
缓冲区由32个字节组成:0x41414142424242,后跟一个专门计算的内核地址和16个零字节。
内核地址是当前线程的ETHEAD对象的PreviousMode成员的位置。将此参数从0x01(UserMode)重写为0x00(KernelMode)将向本机系统服务指示此用户模式线程源于内核模式,并且以内核内存为目标的nt!NtWriteVirtualMemory API的所有后续操作将成功进行。
图2 当前用户模式模块通过易受攻击的驱动程序写入内核内存的能力启用了内核模式
ntdll.dll中的几个低级Windows API函数被重新动态解析:
NtUnloadDriver, NtLoadDriver,NtQuerySystemInformation, NtWriteVirtualMemory, RtlInitUnicodeString, NtOpenDirectoryObject,NtOpenSection, NtMapViewOfSection, NtUnmapViewOfSection and RtlCreateUserThread.
此外,必须满足以下条件才能防止模块过早退出:
不得调试进程(通过检查进程环境块中的标记BeingDebugged)。
•Windows版本必须介于Windows 7.1和Windows Server 2022之间.
接下来是ntoskrnl.exe的内核基址和netoi.sys必须获得(通过使用SystemModuleInformation参数解析NtQuerySystemInformation调用的结果)。这些地址对于以后解析其他内核指针非常重要。
下面是对这个恶意模块进行的内核操作类型的解释。接下来七个部分的编号与u32Flags值中的位字段相对应(见图3)。回想一下,这个位字段返回到加载模块的shell代码,并存储在文件C:\WINDOWS\WINDOWS.ini中。如图1所示。从高级角度来看,该模块负责删除安全解决方案所需的通知,以监控系统内发生的情况,从而标记潜在的恶意行为。
图3 FudModule关闭导出的主要程序
特征
FudModule尝试关闭七个功能。对于每种情况,我们尝试涵盖以下内容:
目的:使用微软GitHub中的一个简单开源驱动程序示例或复杂的封闭软件(如Process Monitor或Windows Defender)来解释高级行为。
核心:显示功能的底层原理,尤其是内核结构。
攻击:详细描述FudModule如何关闭该机制。
影响:演示受影响的内容和不再工作的内容。
0x01: 注册表回调
微软的文档指出“注册表过滤驱动程序 是 过滤注册表调用的任何内核模式的驱动程序”。当WINAPI调用注册表函数时,会通知这些驱动程序。
除了各种安全解决方案之外,微软Sysinternals团队的著名Process Monitor就是一个很好的例子,说明应用程序具有这样的过滤驱动程序并依赖于这样的回调。该工具记录注册表事件(见图4),其中仅包括regedit.exe进程以简化。过滤器排除了所有其他事件类,因为只有注册表开关处于打开状态。
图4:Process Monitor正确记录regedit.exe注册表事件类中的事件。
所有注册表回调都存储在未报告的双链接列表CallbackListHead中。当Process Monitor运行时,至少有两个已注册的回调:它自己的一个回调和属于WdFilter.sys的一个回调。它是Windows Defender的一个组件,请参见图5。请注意,后一个驱动程序也出现在许多其他功能中。
图5:具有WdFilter.sys和Procmon24.sys的两个注册回调结构的双链接列表CallbackListHead。为简单起见,红色箭头仅描绘了列表的一个方向。
因此,FudModule的第一步是获取ntoskrnl.exe内存地址中导出的nt!CmRegisterCallback函数的地址。该过程包含callbackListHead的引用,因此其地址有助于计算感兴趣的双链接列表的位置。
链表以这样的方式清空,即它的尾部指向头部,表示它是空的。因此,依赖于此机制对Windows注册表执行的任何操作的监控都将停止(见图6)。Process Monitor的当前筛选器将显式显示,以演示预期记录的内容,但没有,尽管我们在后台打开和编辑Regedit中的注册表项。
图6:在处理双重链接列表之后,Process Monitor中没有记录任何注册表事件
0x02 对象回调
微软GitHub上的ObCallbackTest解决方案有一个示例驱动程序ObCallabackTest.sys,它演示了如何使用已注册的回调进行进程监控。使用带有相应开关的用户模式可执行文件ObCallbackTestCtrl.exe,可以防止创建(-reject)或终止(-name)所选进程。当我们将后一个开关用于notepad.exe时,用户无法终止该进程,如图7的最后两行所示。
图7 注册控制进程创建的对象回调后,不可能杀死notepad.exe
要成功执行攻击,第一步是找到导出的 nt!ObGetObjectType对象类型函数的地址。接下来,攻击者需要找到一个指向对象回调表的指针,nt!ObTypeIndexTable使用一种算法,使得它的成功不依赖于它运行的Windows版本;有关感兴趣的指针的不同位置,请参见图8。
图8:分别在Windows 7.1和Windows 10 10773中,ntoskrnl.exe的主体nt!ObGetObjectType函数。
此 nt!ObTypeIndexTable 表包含指向所有OBJECT_TYPE结构的指针。每个结构都有一个CallbackList字段,该字段指向已安装回调列表的开头(PsProcessType对象请参见图9)。
图9. PsProcessType是nt!ObTypeIndexTable的OBJECT_TYPE里面的一个。红色突出显示的是一个包含WdFilter.sys和ObCallBackTest.sys的两个回调的双链接列表的方向。
之后,图7 的notepad.exe 不再受到保护,并且我们可以结束它
0x04 进程、映像和线程回调
Windows内核提供了几个与进程相关的通知。您可以运行Process Monitor并跟踪新进程或线程启动时生成的事件,并且加载了可执行图像(如图10所示)。为了简单起见,包括notepad.exe进程。过滤器排除了所有其他事件类,因为只有Process开关处于打开状态。
图10:Process Monitor正确记录notepad.exe的Process事件类中的事件。
回调被组织在三个指针的全局表中,表示为nt!PspCreateThreadNotifyRoutine 、ntPspSetCreateProcessNotifyRoutine和nt!PspLoadImageNotifyRoutine
图11显示了PspLoadImageNotifyRoutine函数表,其中包含了一个名单中的 ahcache.sys的两个回调(深蓝色)和目标Procmon24.sys(红色)。
图11:PspLoadImageNotifyRoutine函数表,包含两个回调,用于名单中的ahcache.sys和目标Procmon24.sys
攻击从解析函数nt!PsSetCreateThreadNotifyRoutine 、nt!PsSetCreateProcessNotifyRoutineEx 和 nt!PsSetLoadImageNotifyRoutine.的内核地址开始,通过算法获得全局指针的地址,因此Windows更新可以保持成功。最后,解析表;在删除回调之前,将执行一项检查,以查看它是否属于表1中所示的名单允许的驱动程序列表。
FudModule似乎关心脱钩操作的安全性;它还解析全局变量nt!PspNotifyEnableMask,它首先为零,因此不会向现有驱动程序发送通知;则清除非名单允许的驱动程序的通知处理程序指针。最后,nt!PspNotifyEnableMask将恢复到其原始值,因此名单允许的驱动程序将继续运行而不会受到影响。
因此,在创建进程或线程时设置通知的安全解决方案将不再是通知此类事件。特别是,Process Monitor不会显示notepad.exe中与流程相关的活动。请参见图12。同样,当前的过滤器被显式显示,以演示什么是预期记录的,但没有记录,尽管我们在后台打开和关闭记事本实例的操作。
图12:记事本没有notepad.exe事件,在删除进程相关回调后记录在进程监视器中。
0x08 非传统微筛选器中的文件系统回调
微软的GitHub中有一个扫描文件系统微过滤器驱动程序解决方案,它演示了微过滤器如何检查文件系统数据。当其用户模式控制台组件scanuser.exe正在运行时,它与 scanner.sys内核驱动程序通信。可以指定拒绝的关键字列表,限制包含这些关键字的任何操作。在我们的例子中,我们选择了EICAR测试字符串。
图13:当文件包含禁止的字符串时,对该文件的写入访问被拒绝。
FudModule旨在通过禁用所有非传统迷你过滤器来关闭此功能。
首先,内核内存地址nt!MmFreeNonCachedMemory获得MmFreeNonCachedMemory是为了计算未导出的nt!MiPteInShadowRange函数的值。接下来,来自fltlib.dll的三个函数的地址,, FilterFindNext, FilterFindFirst 和FilterFindClose被检索去解析包含有关微筛选器和旧筛选器驱动程序的信息的AGGREGATE_ STANDARD_ INFORMATION结构。
微过滤器是一个选项,由操作系统提供给第三方开发人员,代表了传统文件系统过滤器驱动程序的更简单、更健壮的替代品。简单地说,微过滤器是监视或跟踪文件系统数据的Windows文件系统驱动程序,AV和EDR等端点安全产品的组件就是一个典型的例子。
然后,FudModule仅检索非传统微过滤器(由标志FLTFL_ASI_IS_LEGACYFILTER 13设置为false标识),并将其存储在恶意结构内的数组中。此外,令我们惊讶的是,攻击者继续执行非常危险的操作,并在加载的微过滤器中修改许多IRP调度例程(如IRP_MJ_ACQUIRE_for_SECTION_SYNCHRONIZATIONINRP_MJ_CREATE_MAILSLOT、IRP_MJ CREATE、IRP_J_WRITE、IRP_ MJ_ SET_ INFORMATION和IRP MJ_ FILE SYSTEM CONTROL)的PostCall字段。FudModule修改微过滤器函数的序言,以便它们立即返回,而不是处理通知
在恶意软件作者中很少看到内核空间中这种程度的无畏。参见图14。scanner.sys 迷你过滤器被禁用–制作malware.txt,图13可再次访问
图14:scanner.sys运行时修改微型过滤器。左边是原始的序言,右边是修改过的序言。跳过实际的过滤代码并立即返回
0x10 windows 过滤平台标注
Windows过滤平台(WFP)是一组系统服务,为创建网络过滤应用程序提供平台。
WFP调用驱动程序通过处理基于TCP/IP的网络数据扩展了WFP的功能。它们用于深度数据包检查、数据包修改、流修改和数据记录,例如端点安全、HIPS、防火墙和EDR产品。
作为[16]的一部分,有一个名为PacketModificationFilter的项目,这是一个基于WFP标注的最简单的TCP和UDP防火墙,并且有可用的源代码。我们对其进行了自定义,以在通过TCP本地发送时阻止EICAR测试字符串,见图15。
图15:在通过端口12345(上窗格)在本地TCP连接上创建WFP调用后,EICAR测试字符串触发了服务器-客户端连接的阻塞(下窗格)
在其上部窗格中,WFP调用通过端口12345注册为本地TCP连接。在左下窗格中,正在运行的服务器侦听端口12345,在右下窗格中是客户端能够通过相应端口发送消息。首先,发送一个字符串LegimateTraffic以测试通信并成功。接下来,发送禁止的EICAR测试字符串,并进行通信(逻辑在PacketModificationFilter驱动程序中实现,其中EICAR字符串也是硬编码的),并打印错误消息“ConnectionAbortedError[WinError 10053]主机中的软件中止了已建立的连接”。
尽管我们努力理解标注结构,但我们仍然不完全理解其定义。默认的标注结构在子程序netio!InitDefaultCallout中初始化,如图16所示。注意,结构的大小为80字节,初始化将callout标志设置为0x40(第20行)。
图16:Windows 10中的默认调用初始化。结构大小为80字节,标志设置为0x40
但是,在PacketModificationFilter项目中通过 fwpkclnt!FwpsCalloutRegister注册过滤器标注时,仅假定大小为48字节,对于Windows 10 SP3及更高版本,请参见清单1。
图17:标志FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW由FudModule在调用结构中设置。
我们在运行时检查了内存中注册的标注;它们有80字节。在代码中PacketModificationFilter没有设置标志。FudModule修改之后将调用标志中的位t FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW设置(见图17)。这是对所有未列出的驱动程序(见表1)进行的,除了PacketModificationFilternetwork监控第三方供应商安全产品的驱动程序外,还包括其他驱动程序。
为了在内核内存中定位callout结构,模块需要执行几个步骤。首先,它包含导出的 netio!WfpProcessFlowDelete的地址函数。接下来,攻击者需要找到指向对象回调表, netio!gWfpGlobal的指针,同样使用不依赖于Windows版本的算法。
然后,使用恶意软件配置中的版本特定常数u64Offset_Callouts_StructuresPointer和u64Offset_Callouts_NumberofEntries(参见图22)获得调用条目的数量和调用结构数组的指针。最后,根据硬编码常数u32Size_CalloutsEntry计算包含标志的结构成员的位置。
然而,具体的修改并没有改变最初演示的结果,所以攻击者的目标是什么,我们仍然不清楚该功能在何处。
0x20 Windows的事件跟踪句柄
根据微软的文档,Windows事件跟踪(ETW)是一个内核级跟踪模型,它提供了一种机制来跟踪和记录用户模式应用程序和内核模式驱动程序引发的事件。
事件可以实时使用,也可以从日志文件中使用。ETW有三个组件:控制器、提供者和使用者。
由于导出的nt!EtwRegister函数,FudModule导出了所有ETW跟踪提供程序的位置(解析所有对nt!EtwRegister的调用并收集第四个参数,名为RegHandle)。如图18所示,这些句柄包括
nt!EtwpEventTracingProvRegHandle, nt!EtwKernelProvRegHandle,
nt!EtwpPsProvRegHandle, nt!EtwpNetProvRegHandle, nt!EtwpDiskProvRegHandle,
nt!EtwpFileProvRegHandle, nt!EtwpRegTraceHandle, nt!EtwpMemoryProvRegHandle,
nt!EtwAppCompatProvRegHandle, nt!EtwApiCallsProvRegHandle, nt!EtwCVEAuditProvRegHandle,
nt!EtwThreatIntProvRegHandle, nt!EtwLpacProvRegHandle, nt!EtwAdminlessProvRegHandle,
nt!EtwSecurityMitigationsRegHandle and nt!PerfDiagGlobals.
图18: nt!EtwRegister 的第四个参数调用是指向目标句柄的指针
图19说明了将这些感兴趣的句柄归零的模块。这意味着没有任何消费应用程序的系统ETW提供者。这实际上意味着许多相关的ETW监控提供商被禁用。然而,在撰写本文时,我们还没有能够证明这种内核修改的影响。
图19:左侧是FudModule的实现。在右边,它在运行时将所有ETW寄存器句柄归零,只有一个提供程序以红色突出显示
0x40: nt!PfSnNumActiveTraces
预取文件是Windows操作系统的重要组成部分,负责通过缓存进程元数据加快进程创建。
此外,它们也与数字取证相关,因为它们有助于重建事件发生之前和发生期间的时间线。
Lazarus攻击通常涉及使用大量工件和可执行文件。删除这些证据会使调查变得更加困难,但预取文件往往会被留下。WinPrefetchView是读取Windows系统中存储的预取文件并显示其中存储的信息的工具之一。该工具的正常行为如图20的上部窗格所示,捕获了记事本和计算器的执行。
图20:手动超过Nirsoft的WinPrefetchView中允许的轨迹限制之前和之后的预取文件
为了防止创建预取文件,FudModule对几个ntoskrnl.exe程序中引用的全局内核变量nt!PfSnNumActiveTraces感兴趣。(例如,nt!PfSnBeginTrace, nt!PfSnActivateTrace, nt!PfSnDeactivateTrace, nt!PfSnProcessExitNotification and nt!PfFileInfoNotify)。如图21所示,攻击者选择了最后提到的过程来定位nt!PfSnNumActiveTraces的位置,并将其值设置为0xFFFFFF。如果f nt!PfSnNumActiveTrace达到由g_u32Traces_Threshold标识的阈值,nt!PfSnBeginTrace提前退出(与图21中的其他名称不同,这个名称不是来自官方PDB数据库,而是表示我们自己对变量角色的理解)。
图21 如果nt!PfSnNumActiveTraces达到g_u32Traces_Threshold阈值,nt!PfSnBeginTrace函数提前返回
之后,不再跟踪Windows应用程序的执行——请参见图20下方窗格中的空列表框
恶意软件配置
最后,在图22中,我们可以看到模块的完整运行时配置。它作为一个结构存储在其内存地址空间中,包含恶意软件运行所需的所有信息。
它包括DBUtil_ 2_ 3.sys驱动程序的句柄;
其安装路径;
ntoskrnl.exe和netio.sys的模块基地址;
指向已找到的内核变量的指针,如 nt!CallbackListHead、nt!ObTypeIndexTable)和nt!PspNotifyEnableMask。
结构中最多可以存储20个非传统微筛选器的名称,表示它们应该被禁用(上面的Ox08部分)。
ETW提供程序的最多20个内核地址也有空间被置空(上面的部分)。
此外,有多个结构成员表示在不同的Windows版本中变化的重要内核变量的偏移量。恶意软件开发人员已经研究了从7601到20348的大多数Windows版本的正确值。我们展示了一个Windows 7.1版本7601(以紫色突出显示)和Windows 10版本17763(以深蓝色突出显示)的示例。
图22:存储在结构中的FudModule的完整运行时配置。OS版本之间有许多常量不同
相关工作
我们在网上发现的最早提到对象回调(0x02)的是Doug‘Douggem’Confere于2015年5月发布的一篇介绍该概念的博客文章[20],以及Adam Chester于2017年12月发布的博客文章[21],解释了它们在受保护的反病毒进程的反调试技术中的作用。
进程、线程和图像加载通知回调(0x04)在发布于2017年9月[22]的triplefault.io的帖子中进行了分析。
我们想指出克里斯托弗·维拉(Christopher Vella)在[23]中的一次演讲,该演讲涉及禁用类型0x01、0x02和0x04的回调,从而使端点检测和响应(EDR)[24]解决方案总体上变得盲目。
infosec研究员br-sn于2020年8月发布了另一篇关于删除相同类型回调的博客文章[25]。2020年发布了一篇描述微过滤器(0x08)挂钩过程的网络资源[26]。考虑到Windows过滤平台和(0x10),基于WFP的内核驱动程序过滤恶意流量的想法于2012年发布,参见[27]。
关于Windows(0x20)的事件跟踪,我们发现了2021年5月发布的一篇关于中和ETW威胁情报提供商的博客文章[28],以及2015年12月发布的提出基于ETW的日志记录技术的学术论文[29]。
最后,我们没有找到任何解释0x40机制的在线资源。然而,在[30]中,作者在对Windows预取文件的研究中提到了PfSnBeginTrace API函数,这使我们走上了正确的道路(在他们指出前缀Pf表示“预取”之后)。
出于研究目的,在GUI中查看各种内核对象是一个优势。Axt Muller[31]开发了一个名为Windows内核资源管理器的项目,该项目正在积极开发,但已关闭。该项目能够显示内核中所有功能的相应数据,但与0x20和0x40相关的数据除外。
在开源类别中,我们发现了一个由br sn[32]开发的名为CheekyBlinder的项目,部分覆盖了0x01、0x02和0x04功能,但未在许多Windows版本上进行测试,最近一次提交时间为2020年8月。工具EtwExplorer可以显示功能0x20的ETW提供商的数据,请参见[33]。
Pavel Yosifovich[34]的书是Windows内核编程的一个通用资源。在第9章和第10章中,它还解释了这里讨论的许多特性。
Michal Poslusny于2022年1月发表了一篇关于各种易受攻击内核驱动程序的博客,更多相关研究请参见[35]和参考书目部分。
结论
在归因于Lazarus的攻击中,通常有许多工具被分发来危害感兴趣网络中的端点。2021 年10月在荷兰发生的上述案例突出表现在发现了用户模式的FudModule,该模块在内核空间中运行稳定,使用的Windows内部几乎没有文档。攻击者首次在野外利用CVE-2021-21551来禁用所有安全解决方案的监控功能,方法是使用以前不知道或只熟悉专业安全研究人员和(反)欺诈开发人员的机制。在攻击者方面,这无疑需要深入的研究、开发和密集的测试。对于安全研究人员和产品开发人员来说,这应该是重新评估其实现和增强其解决方案自我保护功能的动机。
IOCs
FudModule.dll 97C78020EEDFCD5611872AD7C57F812B069529E96107B9A33B4DA7BC967BF38F
Dbutil_2_3.sys Dell漏洞程序
c996d7971c49252c582171d9380360f2
c948ae14761095e4d76b55d9de86412258be7afd
0296E2CE999E67C76352613A718E11516FE1B0EFC3FFDB8918FC999DD76A73A5