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 协议 ,转载请注明出处!