首页
社区
课程
招聘
[原创]CVE-2021-1732 内核提权详细分析
发表于: 3天前 863

[原创]CVE-2021-1732 内核提权详细分析

3天前
863

对 Windows 内核一直挺感兴趣的,打算先复现CVE并熟悉各种模块......

CVE-2021-1732 中,真正的高危点并非单纯的未初始化内存,而是 win32k 通过 KeUserModeCallback 主动执行用户态回调函数这一设计。KernelCallback 机制使内核在关键对象构造和状态迁移阶段依赖用户态返回的数据,一旦回调入口或回调协议的完整性被破坏(如 KernelCallbackTable 可被劫持、返回数据语义校验不足),将导致内核执行流和对象状态被用户态间接控制。这类漏洞的危害在于其结构性信任失效,而非单一实现缺陷,所以 KernelCallback 路径本身是极具攻击价值的研究方向。

这类漏洞好似 WEB 中前端给后端传值,我抓包修改了值,后端无条件信任了传入的值,导致漏洞产生,所以在这种类似接收参数的接口中一定要做好校验,无论是内核还是WEB,都不要轻易相信任何传来的数据。

漏洞核心不只是“未初始化内存”本身,而是 win32k 的 KernelCallback 信任边界失效:在创建窗口并分配 WndExtra 的流程中,内核会通过 KeUserModeCallback 调用用户态回调(如 xxxClientAllocWindowClassExtraBytes,函数指针来自 PEB->KernelCallbackTable),并将用户态 NtCallbackReturn 返回的数据写回内核对象字段。攻击者在回调窗口期内调用 NtUserConsoleControl 切换窗口的关键标志位(常见描述为 0x800 的 ConsoleWindow 相关语义),导致同一个字段(如 tagWND 中保存 WndExtra 的值)在后续被内核 按“offset(相对 kernel desktop heap base 的偏移)”而非“pointer(用户态指针)”解释。当攻击者再通过 NtCallbackReturn 返回可控数值时,就会出现 字段值与解释语义不同步(out-of-sync) 的类型/语义混淆,最终在 kernel desktop heap 相关地址计算中产生 越界读写(OOB R/W)。利用上通常先把相邻窗口对象的关键字段(如 cbWndExtraspmenu 等)打坏/扩展读写范围,将 OOB 放大为稳定的 任意读 + 任意写,最后通过遍历 EPROCESS->ActiveProcessLinks 找到 PID 4 的 SYSTEM Token 并替换当前进程 Token,实现本地提权到 SYSTEM。

mowenroot/Kernel

HelloWindows.cn - 精校 完整 极致 Windows系统下载仓储站

虚拟机vm搭建的时候添加低权限用户,其他默认

安装完后设置一些基本配置


下载 dControl v2.1(完全禁用 Windows Defender) - 吾爱破解 - 52pojie.cn 并关闭杀毒,防止Exp 被Defender 删了


下载exp

使用 Visual Studio 2019 创建一个新项目,选择 Windows 桌面向导

应用程序类型选择桌面应用程序,并勾选空项目

源文件处右击,添加一个现有项,选择刚下载的 CVE-2021-1732_Exploit.cpp

选择 Debug X64

右击属性,关闭优化

代码生成 -> 运行库选择 多线程调试(/MTd),将运行库静态链接到可执行文件中,否则在虚拟机中运行时可能会报找不到 dll 的错。

链接器 -> 调试 -> 生成经过优化以共享和发布的调试信息 (/DEBUG:FULL),为了后面用 ida 加载 pdb 时有符号。

链接器 -> 高级 ->  随机基址 设置为 否(/DYNAMICBASE:NO),固定基址 设置为 是(/FIXED),这是为了动调的时候方便下断点。

将上诉所有修改应用后,右击项目,然后生成

在项目文件夹中的 X64 -> Debug 文件夹,就能看到生成的 exe 和 pdb 了。

下载 进程资源管理器 - Sysinternals | Microsoft Learn 然后拷贝 procexp 和 Exp 到虚拟机中

右键以管理员身份运行 ProcessExplorer,在上方空白处右键 -> Select Columns,勾选上 Integrity Level,就能看到进程的权限了。

拍摄快照(方便恢复),双击运行 ExploitTest.exe,查看进程权限

让程序继续执行,再看进程权限,已提升为 system

如果蓝屏了,就恢复快照。


下载 dbgview : 0eeK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6H3j5h3&6Q4x3X3g2T1j5h3W2V1N6g2)9J5k6h3y4G2L8g2)9J5c8Y4y4Z5j5i4u0W2i4K6u0r3K9h3&6A6N6q4)9K6c8Y4y4#2M7X3I4Q4x3@1c8Y4P5h3S2V1x3V1#2z5P5f1N6Y4e0$3E0D9c8q4k6a6x3i4S2c8z5p5u0%4i4K6y4r3M7r3q4K6M7%4N6V1i4K6y4p5j5h3q4S2j5b7`.`.

下载 VirtualKD : Release VirtualKD-Redux 2024.3 · 4d61726b/VirtualKD-Redux

把 VirtualKD  拷贝到试验机,并管理员运行 vminstall.exe 进行下载。

下载选项,然后会自动重启。

按 F8选择禁用驱动签名强制,开机即可。

然后在主机上打开 vmmon64.exe,并执行 Run Debugger

会弹出该窗口(假设Break是灰的且Win卡住了,那么需要点击一下Go继续运行Win)

Win10 设置永久禁用驱动程序强制签名。

在用户态使用 SetWindowLong 可以设置扩展内存的数据,最终会在  win32kfull!xxxSetWindowLong 函数中实现,该函数目的 -> 把 dwvalue 写到窗口的扩展内存中,并返回旧值。这里有两种模式,一直是直接用户态空间,还有一种是对内核态空间偏移的内核模式注意这里的 pExtraBytes 为一个内核桌面堆的一个偏移值,原先并不是内存地址(相对于 KernelDesktopHeapBase 的偏移语义)。控制标志位为 ptagWNDK->dwExtraFlag & 0x800

创建窗口可以使用函数 CreateWindowEx 最终会到 win32kfull!xxxCreateWindowEx 使用 win32kbase!HMAllocObject 创建内存后使用tagWND->ptagWNDk->dwExtraFlag &= ~0x40000000u; 进行初始化,这里只是部分初始化,会存在脏数据影响的情况。

win32kbase!HMAllocObject 中使用 RtlAllocateHeap 进行创建空间,这里 flags 为 0,不会清空内存即这里的 ptagWNDK 没有初始化空,但是 ptagWND 是被初始化了在 调用 Win32AllocPoolZInit 手动使用 memset 进行初始化操作。

经过上面分析,不难发现 tagWND->ptagWNDk->dwExtraFlag 标志位存在脏数据干扰的情况,先看看窗口扩展内存是怎么申请的,然后尝试需要寻找能控制这个标志位的函数。

创建扩展内存空间在 win32kfull!xxxCreateWindowEx

先使用重载方法 tagWND::RedirectedFieldcbwndExtra<int>::operator!= 进行判断 ptagWNDk->cbWndExtra != 0, 这里的 cbWndExtra 在注册窗口类的时候可控。

然后调用函数 xxxClientAllocWindowClassExtraBytes(ptagWNDk->cbWndExtra) 进行申请内存。

该函数先 call KernelCallback[123] 进行用户态的内存申请,然后判断返回长度是否为 0x18,并 check 是否可读。

KernelCallback 表,可以在 user32.dll 中寻找到。

UserClientDllInitialize 初始化的 PEB 的时候使用 NtCurrentPeb()->KernelCallbackTable = apfnDispatch;

这里的 123 项为 __xxxClientAllocWindowClassExtraBytes

__xxxClientAllocWindowClassExtraBytes函数实现,就是通过 RtlAllocateHeap 申请用户态内存,但是这里使用 flags 为 8,会内存置零,然后经过 NtCallbackReturn 显式返回内核。内核通过 KernelCallback 进入内核态必须通过 NtCallbackReturn 函数返回。

现在知道窗口的扩展内存 ptagWNDk->cbWndExtra 是怎么申请的了,默认情况下是用户态空间的内存,并不是内核空间地址。先寻找有没有 Api更改扩展内存的标志位(ptagWNDk->dwExtraFlag)。

win32kfull!xxxConsoleControl 中最后的逻辑为:

1、判断了一些标志位和 cbWndExtra 长度

2、如果原本为内核偏移模式,那扩展内存地址 (ptagWNDk->cbWndExtra)直接基于 KernelDesktopHeapBase + pExtraBytes 的偏移,注意这里的 pExtraBytes 为一个内核桌面堆的一个偏移值,原先并不是内存地址(相对于 KernelDesktopHeapBase 的偏移语义)

3、如果原本为用户内存寻址模式,使用 DesktopAlloc 创建新的桌面内核空间,然后拷贝原先的数据并计算到 KernelDesktopHeapBase 的偏移值写到 pExtraBytes(相对于 KernelDesktopHeapBase 的偏移语义) 。

4、最后会更新扩展内存的值,并更改模式。也就是说不管之前是什么模式,最终都会更改为内核桌面偏移的模式(KernelDesktopHeapBase + pExtraBytes + offset)。


传播安全知识、拓宽行业人脉——看雪讲师团队等你加入!

收藏
免费 4
支持
分享
最新回复 (2)
雪    币: 52
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
2
佬,图片长翅膀飞走了
3天前
0
雪    币: 5208
活跃值: (1045)
能力值: ( LV9,RANK:200 )
在线值:
发帖
回帖
粉丝
3
violetrom 佬,图片长翅膀飞走了
啊嘞,师傅刷新试试,我这里显示是正常的,就是排版不好......
2天前
0
游客
登录 | 注册 方可回帖
返回