论坛首页 漏洞分析研究区 阅读主题

[原创]被 Lazarus 组织 长期利用的漏洞:Windows AppLocker 权限提升内核漏洞复现

180 浏览 17 回复
#1 楼主 2026-06-01 21:09:06
CVE-2024-21338 是微软于 2024 年 2 月的周二补丁日披露的 Windows 内核权限提升高危漏洞,CVSS 3.1 评分为 7.8(高危),存在于 Windows 内置的 AppLocker 应用白名单驱动 appid.sys 中。该漏洞的核心危害在于:攻击者可通过精心构造的 IOCTL 请求,在内核态触发任意函数调用,最终从用户模式突破至内核模式执行代码,实现完整的系统控制。更值得关注的是,该0day漏洞在补丁发布前已被朝鲜国家级黑客组织 Lazarus 利用长达半年的时间,用于其 FudModule Rootkit 的内核权限获取,彻底替代了此前易被检测的 BYOVD(自带漏洞驱动) 攻击模式。该漏洞已于 2024 年 2 月 13 日 修复,建议使用 Windows 10 1709 等微软在 2024 年 2 月 13 日 之前停止支持的 Windows 系统版本进行测试。在 Windows 系统中,需要开启 Application Identity 服务,才能利用该漏洞。在 services.msc 中,找到 Application Identity 服务并手动启动。通过拆解0x22A018控制码可知,该控制码需要具有写入权限的句柄,才能成功触发。我们打开 WinObj 工具,找到 AppID,如图所示:可以看出,管理员无权限对设备对象进行写操作,只有在 LOCAL SERVICE 的权限上下文中才具备对设备对象的写操作权限。漏洞的核心缺陷位于appid.sys对 IOCTL 控制码0x22A018的处理函数中,本质是对用户态传入的指针完全缺乏合法性校验,导致任意内核函数调用能力。在(*a2)(userBuffer, v42)中,第一个参数正是内核态从用户态拷贝过来的缓冲区基址。而a2则是userBuffer的0x10偏移,指向一个函数指针,用于调用用户态传入的函数地址。整体调用如下:总的结构体定义如下:注意事项由于appid.sys的设备仅允许 Local Service 进行写访问,攻击者需要获取该身份的模拟令牌管理员无法直接复制 svchost.exe 进程的令牌,所以先获取 SYSTEM 权限。代码如下:通过复制 Winlogon.exe 进程中的 SYSTEM 令牌并模拟权限上下文,再遍历 svchost 进程比对对应 Local Service 的 SID 并复制 LOCAL SERVICE 令牌,最后模拟 LOCAL SERVICE 权限上下文。比对 Local Service SID 的代码如下:我们并不能直接找碎片化的gadget作为原语,因为 KCFG 会验证回调函数的地址是否为合法的内核函数地址,如果不是合法函数地址,执行时就会导致蓝屏。虽然 KCFG 和 SMEP 极大地限制了攻击者通过漏洞执行任意代码的难度,但实际上并不能阻止我们利用漏洞进行提权。因为攻击者只需找到一个符合 KCFG 的合法内核写原语作为我们的回调函数,那这些安全机制自然就被绕过了。在nero22k的这篇文章里讲过可以使用ExpProfileDelete进行内核写操作,将自己线程里的PreviousMode字段通过ObfDereferenceObject将其递减为0,从而将当前线程的权限提升为内核态。虽然这是一个符合 KCFG 的函数,但实际上极力不推荐使用此函数,我通过 IDA 将其反汇编如下:在ExpProfileDelete中,我们发现该函数会检查a1+0x30处是否为0,若为非零值,就会执行该分支。a1是我们传入到内核的缓冲区基址,而我们最多也就传入了0x18或者0x20字节,而a1+0x30处的内存地址不属于我们传入的缓冲区,当对a1+0x30处的内存地址进行解映射操作时,会触发蓝屏。我们用 WinDbg 给ExpProfileDelete函数下个断点:在rcx+0x30处,我们发现该地址的值为0,说明该分支不会执行。会继续执行ObfDereferenceObjectWithTag函数。但当rcx+0x30处的内存地址不为0时,情况就不一样了:如果我们再往下,就会执行第一个分支里面的代码,从而将rcx+0x30处的内存地址解映射,导致蓝屏。值得庆幸的是,我找到了另一个相对更安全的内核写原语,那就是ObpDirectoryTeardownCallback函数,它同样也会调用ObfDereferenceObjectWithTag函数,将其递减为0,从而将当前线程的权限提升为内核态。在找到ObpDirectoryTeardownCallback函数后,我们就可以顺理成章地将(*a2)(userBuffer, v42)的地址指向该函数,从而触发漏洞。你以为这样就完事了吗?那就大错特错了,你确实成功触发了漏洞,但也因此导致了蓝屏或者永远留在了死循环中。请看以下反 C 代码:我们需要给((__int64 (__fastcall *)(__int64, __int64 *, _QWORD, PVOID))v39[1])(v40, &v32, cbInput, pbInput)找一个合适的回调函数地址,使其返回的值小于0,从而跳出这个死循环。我觉得ZwQuerySystemInformation是一个很好的的选择,这个函数指针第一个参数传的就是地址值,而ZwQuerySystemInformation的第一个参数是枚举值,远小于地址值,若第一个参数是无效的枚举值,函数便会直接返回失败,也就是小于0的值,然后直接跳出循环。以我对 Windows 内核的了解,ObfDereferenceObjectWithTag会对目标对象体对应的OBJECT_HEADER结构体中的PointerCount字段也就是在对象体的0x30负偏移处进行递减操作。所以我们在APPID_KERNEL_EXPLOIT第一个字段填写目标PreviousMode字段的地址加上0x30即可将PreviousMode字段递减为0,从而将当前线程的权限提升为内核态。尽管ObpDirectoryTeardownCallback利用方式相对稳健,但仍存在内存篡改风险——具体表现为破坏自身ETHREAD结构体的特定字段。该影响在初始阶段可能不明显,但为实现可靠的权限提升,建议采用双线程架构:主线程首先创建跳板线程(线程A) 和 攻击线程(线程B)线程A 触发漏洞修改自身PreviousMode字段完成内核态提升后,再通过NtWriteVirtualMemory将线程B的PreviousMode字段置为0。线程A 随即退出,隔离内存篡改带来的不稳定因素。调度 线程B 执行后续操作,此时其所有系统调用将被内核判定 为内核模式发起,从而绕过用户态安全检查。 伪代码如下:本文配套的 CVE-2024-21338 Exploit代码,全程由本人独立逆向分析、自主编写实现,无任何开源项目复刻、无第三方代码搬运;其中双线程稳定利用架构、内核原语优化方案等均为个人原创研究成果,完整源码已开源至个人 GitHub仓库 。通过利用 CVE-2024-21338 漏洞,我们可以将自身进程的Protection字段修改为任何值,从而实现进程保护

...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-290662.htm
#2 2026-06-01 21:09:06
2024年的,早被补了吧
#3 2026-06-01 21:09:06
dd
#4 2026-06-01 21:09:06
牛逼
#5 2026-06-01 21:09:06
岚沐

非管理员下似乎没办法直接用sc启动,这个是需要先有管理员权限然后再提升到system权限吗
不是,这个漏洞利用以管理员提升到内核权限为主,并非标准用户提升为管理员
#6 2026-06-01 21:09:06
漫雾.

攻击者也可以通过sc命令自动启用该服务,并非只能手动启用
非管理员下似乎没办法直接用sc启动,这个是需要先有管理员权限然后再提升到system权限吗
#7 2026-06-01 21:09:06
岚沐

攻击场景是不是多用于企业PC,一般用户电脑上不会启用 Application Identity 服务吧,需要手动启用感觉攻击门槛还是太高了
攻击者也可以通过sc命令自动启用该服务,并非只能手动启用
#8 2026-06-01 21:09:06
攻击场景是不是多用于企业PC,一般用户电脑上不会启用 Application Identity 服务吧,需要手动启用感觉攻击门槛还是太高了
#9 2026-06-01 21:09:06
你的帖子非常有用,感谢分享!
#10 2026-06-01 21:09:06
dayang

2024年的,早被补了吧


我在文中已经介绍过该补丁了
#11 2026-06-01 21:09:06
牛逼牛逼
#12 2026-06-01 21:09:06
牛逼牛逼
#13 2026-06-01 21:09:06
好像还挺牛逼的
#14 2026-06-01 21:09:06
感谢分享
#15 2026-06-01 21:09:06
好像还挺牛逼的
#16 2026-06-01 21:09:06
我看看是怎么个回事
‹ 上一页 1 2 下一页 ›

请登录后参与讨论

立即登录 注册账号