CVE-2022-37696 漏洞复现
环境
虚拟机:Win10 21h2 19044.1889
物理机:Win10
debugger:windbg preview
compiler:vs2022
环境我使用的是Win10 21h2
官方8月的版本
简述漏洞
在blf
文件Base Log Record
区域的cbSymbolZone
字段的检测被绕过,从而导致畸形的cbSymbolZone
(即越界的cbSymbolZone
)可以被使用,从而可以在任意偏移位置进行越界写。
这个检测被绕过,跟Base Log Record
区域头的SignaturesOffset
字段相关,原本利用SignaturesOffset
字段进行检测,但是存在另外一个漏洞,利用不正确的SignaturesOffset
,可以进行任意地址写入0xffff
,从而又覆盖SignaturesOffset
高位的2个字节,从而可以使得大数cbSymbolZone
可以绕过检测
漏洞分析
POC 总览
可以通过参考链接的POC.png
对该漏洞的利用进行大概了解
其核心主要在步骤3省略的部分,即对于初始化的MyLog.blf
进行修改,然后再对于已经做了改动的MyLog.blf
进行解析(P.S. 如果v51
无法打开成功的话,其实就是已经被patch
了),并且在此时,把这个文件设置成GENERIC_READ | GENERIC_WRITE | DELETE
最后在MyLog.blf
进行AddLogContainer
会溢出覆盖到之前MyLxg_xxx.blf
的pContainer
指针上,从而导致BSOD
MyLog.blf 的修改
参考链接的对于MyLog.blf
修改的归总如下(P.S. 下图中的0x86C是写错了的,应该是0x868
)
SignaturesOffset
0x0868: |80 79 00 00| => |50 00 00 00|
这个修改不确定有没有用,最终50 00 00 00
会被漏洞修改为50 00 ff ff
从而实现绕过
rgClients
0x09a8: |68 13 00 00| => |30 1B 00 00|
相当于把一个0x1386
的Offset
修改为了0x1B30
,相当于0x1BD8=0x1386+0x870
的Client Context
被弃用,在0x23A0=0x1B30+0x870
的地方,重新伪造了一个Client Context
结构体
cbSymbolZone
0x1B98: |F8 00 00 00| => |4B 11 01 00| cbSymbolZone 0x1114B
改大cbSymbolZone
,以使得AddLogContainer
时,会造成堆越界写
Client Context
0x2390: |00 00 00 00 00 00 00 00| => |B8 1B 00 00 30 1B 00 00|
该字段的修改是为了伪造的FakeClientContext
能过检测
在CLFS_LOG_STATE eState
字段设置为了CLFS_LOG_SHUTDOWN 0x20
畸形 SignaturesOffset 的影响
构造了一个畸形的.blf
,然后再次打开时,会对于该blf
进行解析
HANDLE v51 = CreateLogFile(L"LOG:C:\\Users\\Public\\MyLog", GENERIC_READ | GENERIC_WRITE | DELETE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, OPEN_ALWAYS, NULL);
函数CClfsLogFcbPhysical::Initialize
会进行解析
open evil MyLog.blf
0: kd> k
# Child-SP RetAddr Call Site
00 ffffeb07`b8d56238 fffff807`6e39eaeb CLFS!CClfsLogFcbPhysical::Initialize
01 ffffeb07`b8d56240 fffff807`6e3a0a2b CLFS!CClfsRequest::Create+0x4ef
02 ffffeb07`b8d56390 fffff807`6e3a07f7 CLFS!CClfsRequest::Dispatch+0x97
03 ffffeb07`b8d563e0 fffff807`6e3a0747 CLFS!ClfsDispatchIoRequest+0x87
P.S 以下调用函数都是由CClfsLogFcbPhysical::Initialize
函数直接调用的,都在CClfsLogFcbPhysical::Initialize
函数中
Signatures 数组的 Decode
在CClfsBaseFilePersisted::OpenImage
中,主要会把SignaturesOffset
偏移处的Signatures
数组的内容Decode
写入内存对应位置
下图为CClfsLogFcbPhysical::Initialize
中对于CClfsBaseFilePersisted::OpenImage
的调用
在会走到CClfsBaseFilePersisted::ReadMetadataBlock
时,会调用ClfsDecodeBlock
实现上述Decode
功能
进入ClfsDecodeBlock
时的调用栈如下
0: kd> k
# Child-SP RetAddr Call Site
00 ffffeb07`b6f40f48 fffff807`6e3a4192 CLFS!ClfsDecodeBlock
01 ffffeb07`b6f40f50 fffff807`6e3aa395 CLFS!CClfsBaseFilePersisted::ReadMetadataBlock+0x182
02 ffffeb07`b6f40ff0 fffff807`6e3aa204 CLFS!CClfsBaseFile::AcquireMetadataBlock+0x45
03 ffffeb07`b6f41020 fffff807`6e3a9c36 CLFS!CClfsBaseFilePersisted::ReadImage+0x1e8
04 ffffeb07`b6f41080 fffff807`6e372da2 CLFS!CClfsBaseFilePersisted::OpenImage+0x2fa
05 ffffeb07`b6f41100 fffff807`6e39eaeb CLFS!CClfsLogFcbPhysical::Initialize+0x326
06 ffffeb07`b6f41240 fffff807`6e3a0a2b CLFS!CClfsRequest::Create+0x4ef
ClfsDecodeBlock
函数会循环将signArray
的内容取出,放入base+0x200*N+0x1FE
的地方去,因此0x0050
会被写入13th
((0x68-0x50)/2+1
)的sector
的Signature
地方
P.S 这个0x0050
后续会被写回来;这个 signArray
都是2个字节大小的
P.S. clfs-docs 有对于这个每个sector
的签名数组的详细说明,其实这里的decode
和encode
其实就是:当从磁盘中读blf
时,解析该数组,以恢复这个sector
的内存;从sector
内存获取值,以存储到这个数组中。
When reading the Base Log File from disk, it is critical to parse this array, and take each 2 bytes and overlay them on top of the signature bytes of each corresponding sector (restoring the original data bytes).
Fake Client Context 的解析
在CClfsBaseFile::AcquireClientContext
中,主要会获取Fake Client Context
,从而写到一个sector
的
下图为CClfsLogFcbPhysical::Initialize
中对于CClfsBaseFile::AcquireClientContext
的调用
在CClfsBaseFile::AcquireClientContext
中调用CClfsBaseFile::GetSymbol
获取1st rgClients
的内容
其中对于fake client context
之前的内容修改,就是为了过这里的检测,可以通过查看原来的real client context
查看这个检测,0x13f0 = 0x1368+0x88 | 0x1bd8 = 0x1368+0x870
,因此最后的地方应该是0x1b30+0x88=0x1bb8
ResetLog 覆盖 sector 的 signature
这里v75
为fake client context
,为了能走到CClfsLogFcbPhysical::ResetLog
,则需要将其设置为0x20
,且使CClfsLogFcbPhysical:IsMultiplexed
返回false
在这里,由于修改了Fake Client Context
的偏移,因此最后CLFS_LSN_INVALID:0xffffffff
恰好写入了14th sector
的signature
位置
overwrite Signatures Offset
最后这个通过间接跳转走到ClfsEncodeBlock
的地方,然后通过encode
把0x0050
和0xffff
写回,最后导致signaturesOffset
的值就被修改成了不合理的超大值
整体的调用链如下所示
0: kd> k
# Child-SP RetAddr Call Site
00 ffffeb07`b6f40fd8 fffff807`6e3a2232 CLFS!ClfsEncodeBlock
01 ffffeb07`b6f40fe0 fffff807`6e3999a0 CLFS!CClfsBaseFilePersisted::WriteMetadataBlock+0x152
02 ffffeb07`b6f41070 fffff807`6e37161f CLFS!CClfsBaseFilePersisted::FlushImage+0x40
03 ffffeb07`b6f410b0 fffff807`6e373701 CLFS!CClfsLogFcbPhysical::FlushMetadata+0xef
04 ffffeb07`b6f41100 fffff807`6e39eaeb CLFS!CClfsLogFcbPhysical::Initialize+0xc85
05 ffffeb07`b6f41240 fffff807`6e3a0a2b CLFS!CClfsRequest::Create+0x4ef
06 ffffeb07`b6f41390 fffff807`6e3a07f7 CLFS!CClfsRequest::Dispatch+0x97
最后的写回signArray
可以看到最后被修改成了0xffff0050
畸形的 SignatureOffset 影响的整个过程总结
解析signArray
,向13th sector
写入0x0050
,解析fake client context
,使得14th sector
写入0xffff
,最后ClfsEncodeBlockPrivate
,写回signArray
,从而构造了一个超大值signaturesOffset
POC 分析
重新来看这个POC
的图
- 初始化
MyLog.blf
- 堆风水布局,以使得后面的
MyLog
的堆块和MyLxg_xxx
的堆块是连续的 - 构造畸形的
MyLog.blf
,解析时,仍能打开HANDLE
- 构造与
MyLog
堆块连续的堆块MyLxg_xxx
- 给
MyLxg_xxx
添加LogContainer
,在这个结构体中存在一个函数指针pContainer
- 获取
NtSetInformationFile
的地址 - 给
MyLog
添加LogContainer
,此时已经可以溢出到MyLxg_xxx
的Container Context
的pContainer
- 通过
NtSetInformationFile
设置MyLxg_xxx
为FileDispositionInformation
,即该函数将在文件关闭时删除文件或取消先前请求的删除,因此在关闭文件时,可以触发到removeContainer
对pContainer
的使用,从而触发BSOD
- 触发漏洞
在扩大这个漏洞影响时,是覆盖了SignaturesOffset
,而在CClfsBaseFilePersisted::AllocSymbol
时,则利用SignaturesOffset
做的检测
因此此时,可以直接绕过这个检测,使得cbSymbolZone
可以为一个大值,从而覆盖到下一个堆块的pContainer
最后漏洞触发是在关闭blf
文件时,在函数CClfsBaseFilePersisted::RemoveContainer
中,会直接使用pContainer
的虚表进行函数调用,而被破坏的pContainer
会导致BSOD
甚至EOP
patch
该漏洞的patch
,在CClfsBaseFilePersisted::LoadContainerQ
时,对于cbSymbolZone
和signaturesOffset
进行了检测
1: kd> k
# Child-SP RetAddr Call Site
00 ffff810c`a0c44fa0 fffff805`373b2176 CLFS!CClfsBaseFilePersisted::LoadContainerQ+0x1b2
01 ffff810c`a0c45100 fffff805`373df093 CLFS!CClfsLogFcbPhysical::Initialize+0x6da
02 ffff810c`a0c45240 fffff805`373e0aeb CLFS!CClfsRequest::Create+0x4ef
CClfsBaseFilePersisted::LoadContainerQ
函数在CClfsLogFcbPhysical::Initialize
函数中,位于ResetLog
之后,ClfsEncodeBlock
之前
总结
该漏洞利用不正确的SignaturesOffset
,仅仅能写0xffffffff
的利用,扩大到再次修改SignaturesOffset
,从而直接导致溢出
参考
https://www.zscaler.com/blogs/security-research/technical-analysis-windows-clfs-zero-day-vulnerability-cve-2022-37969-part
https://github.com/ionescu007/clfs-docs/blob/main/README.md
https://www.slideshare.net/PeterHlavaty/deathnote-of-microsoft-windows-kernel
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!