基础
披露或补丁日期: 2023 年 9 月 12 日
产品:窗户
咨询: https://msrc.microsoft.com/update-guide/vulnerability/CVE-2023-36802
受影响的版本:
-
没有 KB5030211 或 KB5030214 的 Windows 10
-
没有 KB5030219 或 KB5030217 的 Windows 11
-
没有 KB5030214 的 Windows Server 2019
-
没有 KB5030216 或 KB503025 的 Windows Server 2022
第一个补丁版本:
-
带有 KB5030211 或 KB5030214 的 Windows 10
-
带有 KB5030219 或 KB5030217 的 Windows 11
-
Windows Server 2019 与 KB5030214
-
带有 KB5030216 或 KB503025 的 Windows Server 2022
问题/错误报告:不适用
补丁 CL:不适用
Bug 引入 CL: N/A
记者:
-
夏光辉 (@ze0r) 与河北华测
-
Quan Jin (@jq0904) 和 ze0r 与 DBAPPSecurity WebBin 实验室
-
Valentina Palmiotti 与 IBM X-Force
-
微软威胁情报
-
微软安全响应中心
代码
概念验证:
HANDLE h;
HRESULT hr;
DWORD bytesReturned;
char buf[0x100] = {0};
hr = KsOpenDefaultDevice(KSNAME_Server, GENERIC_READ | GENERIC_WRITE, &h);
memset(buf, 'A', sizeof(buf));
*(int32_t *)buf = 1;
*((int64_t *)buf + 3) = 0;
BOOL status = DeviceIoControl(h, IOCTL_FRAMESERVER_INIT_CONTEXT, &buf, sizeof(buf), &buf, sizeof(buf), &bytesReturned, 0);
memset(buf, 'A', sizeof(buf));
*((DWORD*)buf + 8) = 1;
*((DWORD*)buf + 9) = 1;
status = DeviceIoControl(h, IOCTL_FRAMESERVER_PUBLISH_RX, &buf, sizeof(buf), &buf, sizeof(buf), &bytesReturned, 0);
利用样本:未公开
您在进行分析时是否有权访问漏洞利用样本?是的
漏洞
Bug 类别:类型混淆
漏洞详细信息:
当在设备驱动IOCTL_FRAMESERVER_PUBLISH_RX
程序上执行 IOCTL时mskssrv
,会执行一个调用,而FSRendezvousServer::PublishRx()
该调用又会调用FSStreamReg::PublishRx()
. 将获取the 字段FSStreamReg::PublishRx()
中的任何内容并将其用作进一步的对象,只要它是有效对象。FsContext2
FILE_OBJECT
FsStreamReg
攻击者可以在同一设备句柄上IOCTL_FRAMESERVER_PUBLISH_RX
使用IOCTL,这会将字段初始化为不同类型(即 类型)的对象。IOCTL_FRAMESERVER_INIT_CONTEXT
FsContext2
FsContextReg
FSStreamReg::PublishRx()
对它认为是FsStreamReg
对象的对象执行多项操作,其中一些操作会导致可利用的场景,例如越界写入。
其他 3 个 IOCTL 中也存在类似的漏洞:
-
IOCTL_FRAMESERVER_PUBLISH_TX
-
IOCTL_FRAMESERVER_CONSUME_TX
-
IOCTL_FRAMESERVER_CONSUME_RX
所有这些都通过 CVE-2023-36802 标识符引用。
补丁分析:
在补丁之前,FSRendezvousServer::PublishRx()
会FSRendezvousServer::FindObject()
在调用之前调用FSStreamReg::PublishRx()
。仅当FSRendezvousServer::FindObject()
返回TRUE
,FSStreamReg::PublishRx()
时才被调用。但是,FSRendezvousServer::FindObject()
不会检查对象的类型。
该补丁更改了FSRendezvousServer::FindObject()
检查对象的类型字段是否专门设置为2,否则返回FALSE
。FsStreamReg
构造对象时(在 中) ,类型字段设置为 2 FSStreamReg::FSStreamReg()
。此外,FSRendezvousServer::FindObject()
还被重命名为更合适的名称FSRendezvousServer::FindStreamObject()
。
关于如何发现此漏洞的想法(模糊测试、代码审计、变体分析等):
该错误可以通过手动代码审核或模糊测试来发现。查看上面的概念证明,两个 IOCTL 与相对简单的输入缓冲区的组合会触发崩溃。Valentina Palmiotti 通过手动代码审核发现了此漏洞,如她的博客文章中所述。
(历史/现在/未来)错误的背景:
-
2023 年 6 月 16 日:
mskssrv.sys
Synacktiv 在 Pwn2Own 2023 上使用的另一个权限提升漏洞 CVE-2023-29360 已被修补并公开披露。尽管在同一驱动程序中,CVE-2023-29360 与 CVE-2023-36802 截然不同。 -
2023 年 9 月 12 日:CVE-2023-36802 已修补并公开披露,据报道已在野外发现。
-
2023 年 10 月 10 日:Valentina Palmiotti发布了CVE-2023-36802 及其利用的详细信息。
漏洞利用
(此处定义了术语“利用原语”、“利用策略”、“利用技术”和“利用流程”。)
利用策略(或多个策略):
-
通过众所周知的技术泄露相关内核对象的地址
NtQuerySystemInformation
。 -
创建内核池布局,以便将来的
FSContextReg
分配落在被攻击者控制的数据包围的洞中。 -
分配一个
FSContextReg
对象(通过IOCTL_FRAMESERVER_INIT_CONTEXT
IOCTL)。 -
对对象执行越界操作
FSContextReg
(通过IOCTL_FRAMESERVER_PUBLISH_RX
),这将执行一些有趣的写入并最终将 的 设置PreviousMode
为KTHREAD
0。 -
在内核地址上使用
NtReadVirtualMemory
和NtWriteVirtualMemory
将系统令牌复制到EPROCESS
当前进程的 。 -
清理。
-
产生感兴趣的命令,例如
cmd.exe
.
利用流程:
根据 Windows 版本的不同,分析的野生样本中存在两种不同的漏洞利用流程。这是因为根据 Windows 版本:
-
代码
FSStreamReg::PublishRx
略有不同,即最新版本包含对对象字段之一的mskssrv.sys
调用,而旧版本则不包含ObfDereferenceObject
FsStreamReg
-
对象的结构布局
FsStreamReg
略有不同。这对于漏洞利用流程中引起的副作用很重要。
由于较新的mskssrv.sys
版本包含对 的方便调用ObfDereferenceObject
,因此它将使用该调用将PreviousMode
字段直接递减至 0。我们将首先查看此漏洞利用流程。
由于旧mskssrv.sys
版本不包含方便的ObfDereferenceObject
调用,因此利用流程有点复杂。它会破坏 CLFS 特定的对象,以通过 vtable 调用来调用内核小工具。
无 CLFS 的漏洞利用流程
-
mskssrv
通过调用打开设备KsOpenDefaultDevice
。 -
通过以下方式泄漏一些所需的内核地址
NtQuerySystemInformation(SystemExtendedHandleInformation, ...)
: -
当前
KTHREAD
地址(以及当前PreviousMode
地址) -
EPROCESS
系统进程和当前进程的地址(以及它们各自的Token
地址) -
FILE_OBJECT
mskssrv
设备文件句柄的地址 -
使用众所周知的
NtFsControlFile
使用 fsctl 代码 0x119ff8进行调用的命名管道技术,将大小为 0x80 的对象(不包括 0x10 字节池标头)喷射到池中。 -
关闭一些管道,在泳池喷雾上打孔。
-
调用
IOCTL_FRAMESERVER_INIT_CONTEXT
ioctl: -
FsInitializeContextRendezVous()
来电FSRendezvousServer::InitializeContext()
-
FSRendezvousServer::InitializeContext()
分配一个FsContextReg
大小为0x78字节的对象,并将 中的FsContext2
字段设置为FILE_OBJECT
指向该对象。该FsContextReg
对象将占据先前创建的孔的位置。 -
重新填满剩余的孔。
-
在单独的线程中调用
IOCTL_FRAMESERVER_PUBLISH_RX
ioctl : -
FSRendezvousServer::PublishRx()
随后将调用和FSStreamReg::PublishRx()
。 -
FSStreamReg::PublishRx()
将期望FsStreamReg
字段中存在一个FsContext2
大小为 0x1d8 字节的对象,而实际上FsContextReg
存在一个较小的对象,与受控数据相邻。 -
FSStreamReg::PublishRx()
然后将调用ObfDereferenceObject()
从相邻对象中取出的超出范围的字段。攻击者将PreviousMode
当前线程的地址放在那里。这会将主线程的 递减PreviousMode
至 0。此时,攻击者可以从主线程调用内核地址上的NtReadVirtualMemory
和。NtWriteVirtualMemory
-
如果不加以处理,
FSStreamReg::PublishRx()
现在将调用KeSetEvent
另一个越界字段,该字段与ProcessBilled
相邻对象(不受攻击者控制)的池标头中的字段一致。由于这是一个无效的指针,它会对系统进行错误检查。为了防止这种情况,攻击者保持FSStreamReg::PublishRx()
锁定在 while 循环中。这是通过伪造自引用链表条目来实现的。FSFrameMdlList::MoveNext
将继续返回相同的列表条目。列表条目被放置在用户模式中,因此该漏洞可以根据需要打破这个 while 循环(见下文)。 -
同时在主线程中:
-
尝试
NtReadVirtualMemory
在循环中使用读取系统进程令牌,直到成功。PreviousMode
在另一个线程中被覆盖后,这将成功。 -
使用将系统令牌写入
EPROCESS
当前进程的NtWriteVirtualMemory
。 -
FsContext2
从 中读取该字段的值FILE_OBJECT
即可获取该对象的地址FsContextReg
。 -
读取(以便以后恢复)并将
ProcessBilled
相邻池块标头之一覆盖为 NULL,因此KeSetEvent
不会使系统崩溃。 -
FSStreamReg::PublishRx()
通过更改自引用列表条目来打破当前锁定的 while 循环。FSStreamReg::PublishRx()
现在将继续但不会调用,KeSetEvent
因为该字段为 NULL。 -
等待
FSStreamReg::PublishRx()
另一个线程完成。 -
将损坏的
ProcessBilled
字段恢复为其原始值。 -
增加当前的引用计数
EPROCESS
。 -
将 重置
PreviousMode
为 1。 -
启动感兴趣的命令,例如
cmd.exe
.
使用 CLFS 来利用流程
在较旧的系统上,FSStreamReg::PublishRx
不包含对ObfDereferenceObject
. 这是不幸的,因为ObfDereferenceObject
这是一个非常容易减少PreviousMode
.
但是,FSStreamReg::PublishRx()
仍然会调用FSFrameMdl::UnmapPages()
从相邻(喷射的)对象中获取的超出范围的地址,这将在超出范围的地址上进行一些有用的写入:
-
在该地址的偏移 0xc8 处写入 QWORD 0
-
在该地址的偏移 0x10 处写入 DWORD 2
该漏洞如何利用这一点?
-
以与无 CLFS 漏洞利用流程相同的方式泄漏所需的内核地址。
-
创建一个 CLFS 日志文件,打开它并泄漏其内核地址(使用
NtQuerySystemInformation(SystemExtendedHandleInformation, ...)
)。 -
解决一些
nt
内核小工具:PoFxProcessorNotification
,IoSizeofWorkItem
和RtlClearBit
-
CClfsContainer
在 0x1000000 处伪造一个假对象,在 0x1000800 处伪造一个虚函数表。 -
在 0x1000400 处伪造一个假的
BitMapHeader
,并使用指向该地址的位图指针PreviousMode
。 -
喷射池(再次使用
NtFsControlFile
),但精心制作的对象现在包含 CLFS 日志文件的内核地址 + 0x2c9。 -
创建孔,分配
FsContextReg
孔中的对象并重新填充剩余的孔,就像以前一样。 -
触发
FSStreamReg::PublishRx()
。FSFrameMdl::UnmapPages()
将在该地址读取越界时调用,因此写入: -
QWORD 0 位于内存中 CLFS 日志文件开头的偏移量 0x2c9+0xc8=0x391 处
-
DWORD 2 位于内存中 CLFS 日志文件开头偏移量 0x2c9+0x10=0x2d9 处
-
调用
CreateLogFile
最终会调用CClfsBaseFilePersisted::CheckSecureAccess
。 -
ClfsBaseFilePersisted::CheckSecureAccess
将使用偏移量 0x398 处的 DWORD 作为距日志块头(大小为 0x70)末尾的偏移量来查找对象CLFS_CONTAINER_CONTEXT
(通过调用CClfsBaseFile::GetSymbol()
)。但是 QWORD 写入损坏了该 DWORD 的最低有效字节,这使得偏移量从 0x1460 更改为 0x1400。因此,现在 CLFS 日志文件+0x70+0x1400 中的任何内容都将被解释为CLFS_CONTAINER_CONTEXT
对象,即攻击者控制的数据。CClfsContainer
在该对象的偏移量 0x18 处,取消引用指向对象的指针。该漏洞利用程序将地址 0x1000000 作为指针,之前它在那里准备了一个伪造的CClfsContainer
对象。 -
CLFS!CClfsBaseFilePersisted::CheckSecureAccess
然后将调用该对象的 vtable 中的第一个函数(在正常情况下CClfsContainer::AddRef
),并将对象本身作为第一个参数传递。该漏洞利用将该nt!PoFxProcessorNotification
函数放置在伪造的 vtable 中。(请注意,CLFS 驱动程序中有 CFG,因此该漏洞必须使用允许的函数地址作为小工具。) -
nt!PoFxProcessorNotification
将取消引用其参数的几个地址,并使用这些地址中的另一个作为参数来调用这些地址之一。所以现在漏洞利用控制了被调用的函数及其第一个参数。该漏洞利用程序选择nt!RtlClearBit
要调用的函数。 -
nt!RtlClearBit
需要 2 个参数:指向位图标头的指针(包含指向位图本身的指针)和要清除的位数。回想一下,该漏洞利用程序准备了一个伪造的位图标头,其中有一个指向该地址的位图指针PreviousMode
。第二个参数 - 要清除的位数 - 不受控制,但rdx
可以方便地设置为 0。nt!RtlClearBit
因此将将该PreviousMode
字段设置为 0。 -
为了稳定性,该漏洞利用程序准备了第二个 vtable 条目
nt!IoSizeofWorkItem
- 它除了设置之外什么也不做eax
- 因为稍后CClfsBaseFilePersisted::CheckSecureAccess
会调用它认为的内容CClfsContainer::Release
。 -
将系统进程令牌复制到当前的
EPROCESS
. -
恢复 CLFS 日志文件中原始的 0x1460 偏移量。
-
恢复
PreviousMode
为1。 -
启动感兴趣的命令,例如
cmd.exe
.
请注意,在这个版本的mskssrv.sys
漏洞利用流程中,漏洞利用程序不必处理尴尬的KeSetEvent
调用。原因是KeSetEvent
旧版本中传递给字段的偏移量mskssrv.sys
并不落在池块标头内,而是落在受控喷射数据中,并以这种方式设置为 NULL。
与 Valentina Palmiotti 的漏洞利用存在显着差异
了解上述自然利用与 Valentina Palmiotti 在她的博客文章中描述的利用之间的差异是一个有趣的练习。
Valentina 的漏洞利用基于最新mskssrv.sys
版本。可以从ObfDereferenceObject
屏幕截图中是否存在呼叫来推断。因此,让我们将她的漏洞利用与“无 CLFS 漏洞利用流程”进行比较。
-
Valentina 使用调用提供的“write 2 where”原语
FSFrameMdl::UnmapPages()
,“与 I/O 环技术结合”。我们分析的漏洞利用了旧mskssrv.sys
版本的原语(与 CLFS 技术结合),但ObfDereferenceObject()
在新mskssrv.sys
版本中使用了调用提供的“递减位置”原语。 -
Valentina 结合信息泄漏进行了一些更复杂的池整理,
FSStreamReg::GetStats
以防止稍后的KeSetEvent
调用因无效指针而崩溃。这里分析的漏洞使用了不同的解决方案:它FSStreamReg::PublishRx
从单独的线程触发该函数,并将其保持在锁定的 while 循环中(在用户模式下使用自引用链表条目),从而阻止调用到达KeSetEvent
。一旦它具有内核读/写,它就会将触发 的字段更改KeSetEvent
为 NULL,然后更改自引用链表条目,以便 while 循环中断。这意味着KeSetEvent
呼叫将被跳过。(之后它恢复会触发调用的字段,KeSetEvent
因为它是池块中的拥有进程 EPROCESS)。
相同漏洞利用流程的已知案例:
正如Zscaler 的博客文章所述,野外利用的 CVE-2022-37969 CLFS 漏洞遵循非常相似的利用流程。
漏洞利用链的一部分?此漏洞很可能被用作独立的本地权限升级。
下一步
变异分析
变体分析的领域/方法(以及原因):鉴于此漏洞以及 CVE-2023-29360 相对简单的性质,更多的模糊测试和代码审核可能会mskssrv.sys
产生更多错误。
发现的变种:不适用
结构改进
有哪些结构性改进,例如消除 bug 类、防止引入此漏洞、减轻漏洞利用流程、使此类漏洞更难利用等方法?
杀死 bug 类的想法:
CastGuard可能会捕获此错误,因为看起来(基于它们的 vtable)FsStreamReg
和FsContextReg
type 都派生自同一个FsRegObject
类。
减少漏洞利用流程的想法:
-
MTE和CHERI不会捕获类型混淆,但会捕获类型混淆导致的越界访问。
-
在无 CLFS 漏洞利用流程中
FSStreamReg::PublishRx
取消引用假指针时,SMAP将捕获用户模式访问。FSFrameMdl
在 CLFS 漏洞利用流程中,SMAP 将捕获CClfsContainer
用户模式下的虚假对象 vtable 访问。 -
通过调用消除内核地址泄漏
NtQuerySystemInformation
。
其他潜在的改进:
不适用
0day检测方法
类似 0day 的潜在检测方法有哪些?这意味着是否有任何关于如何将此漏洞或类似漏洞检测为 0day 的想法?
-
使用的漏洞利用技术的静态签名(例如使用 Yara)。
-
根据有趣的动态信号分析样本,例如
NtQuerySystemInformation
使用SystemExtendedHandleInformation
参数的调用。
想阅读原文可通过点击阅读原文
感谢您抽出
.
.
来阅读本文
点它,分享点赞在看都在这里
原文始发于微信公众号(Ots安全):野外0day - CVE-2023-36802:Microsoft 流服务代理权限提升漏洞
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论