当我最初为CF的Windows研究员职位面试候选人时,我给出的一个挑战与CVE-2024-21338有关。这是一个Windows内核权限提升漏洞,具体来说是appid.sys
驱动程序中的一个不受信任指针解引用漏洞。该驱动程序负责AppLocker技术。
当时,由于Avast在Lazarus FudModule Rootkit上的出色工作,这个漏洞变得非常出名。
这是我们新聘研究员的详细写作报告,欢迎加入团队!🙂
概要
根据Avast的详细帖子,漏洞的主要要点总结如下:
-
该漏洞存在于 AppHashComputeImageHashInternal()
函数中,可以通过向名为\DeviceAppid
的设备对象发送值为0x22A018
的IOCTL来调用。 -
驱动程序期望从IOCTL的输入缓冲区引用两个指针。 -
由于我们完全控制了指令指针和第一个参数中的数据,这个漏洞提供了一个强大的原语。 -
根据设备对象名上存在的ACL,只有 LOCAL SERVICE
和AppIDSvc
用户有足够的权限发送目标IoControlCode
。 -
目标驱动程序 appid.sys
不会自动加载,需要向特定的与AppLocker相关的ETW提供程序发送事件。
挑战
SMEP和kCFG
-
尽管我们完全控制了指令指针,但由于存在监督模式执行防护(SMEP)缓解措施,我们不能直接提供用户模式指针来直接执行我们的shellcode。 -
回调函数是间接调用的,因此我们必须找到一个内核空间中的有效指针来绕过内核控制流防护(kCFG)的保护。
KASLR
由于这个漏洞在LocalService
用户的上下文中运行,KASLR在这里不是一个大问题。借助于常青的NtQuerySystemInformation()
系统调用,我们可以轻松泄漏几乎所有构建漏洞利用所需的内核地址。
加载目标驱动程序
由于驱动程序默认未加载,我们可以手动加载它,方法是使用服务管理器或向与AppLocker相关的ETW提供程序发送事件以启动AppID服务。
为了测试目的,该驱动程序已经加载到内核空间。
利用
根本原因分析
当接收到值为0x22A018
的IOCTL时,会调用AipSmartHashImageFile()
函数来处理此控制代码。在Windows 11中,第一个参数应为以下结构的指针:
typedef struct _HASH_IMAGE_FILE {
PVOID ImageContext; // 指向哈希文件映像上下文的指针
FILE_OBJECT *FileObject; // 文件的内核对象指针
PVOID CallbackTable; // 指向回调函数的指针
ULONGLONG Action; // 不太确定
} HASH_IMAGE_FILE, *PHASH_IMAGE_FILE;
然后调用AppHashComputeFileHashesInternal()
函数来计算目标文件的哈希值。传递给这个函数的前两个参数是用户可控的:
稍后,经过一些初始化,AppHashComputeFileHashesInternal()
函数调用AppHashComputeImageHashInternal()
来获取哈希值,前两个参数直接传递给目标函数。
在AppHashComputeImageHashInternal()
函数中,在计算目标映像的哈希值之前,它会调用来自第二个参数指针的两个回调函数,这些指针完全由用户控制。因此,导致了一个完全的RIP接管条件。
总体而言,对易受攻击函数的调用图如下所示:
SMEP和kCFG绕过
由于存在SMEP和kCFG,如果我们直接从用户模式调用随机的ROP gadget或shellcode,内核会因为bug检查而退出。相反,我们必须找到一些有用的函数来帮助我们执行仅数据攻击。
在阅读了一些文章后(1,2),我发现nt!SeSetAccessStateGenericMapping()
函数被广泛用于绕过kCFG。然而,它要求第一个参数指向一个至少大小为0x50
的结构:
同时,目标IOCTL要求输入指针的大小为0x20
(_HASH_IMAGE_FILE结构的大小):
所以我决定自己找一个gadget。
花了一些时间寻找基于给定约束条件执行有趣操作的小函数,我找到一个有效的候选函数:nt!DbgkpTriageDumpRestoreState
。这个gadget允许在第一个指针的偏移0x2078
处覆盖8字节(这个结构是ImageContext指针),并使用输入结构的偏移0x10
(在本例中为CallbackTable字段)处的值:
构建漏洞利用
有两种可能的方法可以使用上述gadget来利用此漏洞:
-
设置目标 KTHREAD
的PreviousMode
字段,然后滥用NtReadProcessMemory/NtWriteProcessMemory
系统调用来实现内核空间的任意读/写。在Avast的报告中,Lazarus使用了这种技术。 -
覆盖当前进程令牌的 _SEP_TOKEN_PRIVILEGES
结构以启用SeDebugPrivilege
,并利用此权限在特权进程中注入shellcode。这种方式需要触发两次漏洞,以便覆盖Present
和Enabled
字段。
尽管写入的值也是_HASH_IMAGE_FILE结构的CallbackTable字段的指针,但它必须是一个有效的指针值。然而,通过利用VirtualAlloc
API,仍然可以生成一个合适的写入值,因为上述两种方法都不需要(太)具体的值。在我的漏洞利用中,我展示了两种方法来获得SYSTEM shell。
漏洞利用代码
你可以在https://github.com/Crowdfense/CVE-2024-21338/blob/main/CVE-2024-21338.cpp找到漏洞利用代码。
演示
POC 1 – 滥用PreviousMode
POC 2 – 滥用SeDebugPrivilege
参考资料
-
https://decoded.avast.io/janvojtesek/lazarus-and-the-fudmodule-rootkit-beyond-byovd-with-an-admin-to-kernel-zero-day/
-
https://decoded.avast.io/luiginocamastra/from-byovd-to-a-0-day-unveiling-advanced-exploits-in-cyber-recruiting-scams/
-
https://github.com/sam-b/windows_kernel_address_leaks
-
https://research.nccgroup.com/2020/05/25/cve-2018-8611-exploiting-windows-ktm-part-5-5-vulnerability-detection-and-a-better-read-write-primitive/#previousmode-abuse:~:text=into%20PreviousMode%20further.-,PreviousMode%20%E2%80%93%20a%20%22god%20mode%22%20primitive%3F,-PreviousMode%20on%2064
-
https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/previousmode
-
https://nero22k.github.io/posts/windows-applocker-driver-elevation-of-privilege-cve-2024-21338/
-
https://labs.bluefrostsecurity.de/blog/2020/01/07/cve-2019-1215-analysis-of-a-use-after-free-in-ws2ifsl/
-
https://ti.qianxin.com/blog/articles/CVE-2023-28252-Analysis-of-In-the-Wild-Exploit-Sample-of-CLFS-Privilege-Escalation-Vulnerability/
原文始发于微信公众号(3072):CVE-2024-21338 Windows AppLocker LPE 漏洞分析