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对该漏洞的利用进行大概了解

POC

其核心主要在步骤3省略的部分,即对于初始化的MyLog.blf进行修改,然后再对于已经做了改动的MyLog.blf进行解析(P.S. 如果v51无法打开成功的话,其实就是已经被patch了),并且在此时,把这个文件设置成GENERIC_READ | GENERIC_WRITE | DELETE

最后在MyLog.blf进行AddLogContainer会溢出覆盖到之前MyLxg_xxx.blfpContainer指针上,从而导致BSOD

MyLog.blf 的修改

参考链接的对于MyLog.blf修改的归总如下(P.S. 下图中的0x86C是写错了的,应该是0x868

010

diff

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|
相当于把一个0x1386Offset修改为了0x1B30,相当于0x1BD8=0x1386+0x870Client 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能过检测

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的调用

OpenImageIDA

在会走到CClfsBaseFilePersisted::ReadMetadataBlock时,会调用ClfsDecodeBlock实现上述Decode功能

ReadMetadataBlock

进入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)的sectorSignature地方

ClfsDecodeBlockIDA

ClfsDecodeBlockWindbg

P.S 这个0x0050后续会被写回来;这个 signArray 都是2个字节大小的

P.S. clfs-docs 有对于这个每个sector的签名数组的详细说明,其实这里的decodeencode其实就是:当从磁盘中读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的调用

AcquireClientContextIDA

CClfsBaseFile::AcquireClientContext中调用CClfsBaseFile::GetSymbol获取1st rgClients的内容

GetSymbolIDA

其中对于fake client context之前的内容修改,就是为了过这里的检测,可以通过查看原来的real client context查看这个检测,0x13f0 = 0x1368+0x88 | 0x1bd8 = 0x1368+0x870,因此最后的地方应该是0x1b30+0x88=0x1bb8

RealClientContext

ResetLog 覆盖 sector 的 signature

这里v75fake client context,为了能走到CClfsLogFcbPhysical::ResetLog,则需要将其设置为0x20,且使CClfsLogFcbPhysical:IsMultiplexed返回false

eStateIDA

在这里,由于修改了Fake Client Context的偏移,因此最后CLFS_LSN_INVALID:0xffffffff恰好写入了14th sectorsignature位置

ResetLogIDA

overwrite Signatures Offset

最后这个通过间接跳转走到ClfsEncodeBlock的地方,然后通过encode0x00500xffff写回,最后导致signaturesOffset的值就被修改成了不合理的超大值

FlushMetadataIDA

整体的调用链如下所示

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

ClfsEncodeBlockPrivateIDA

可以看到最后被修改成了0xffff0050

畸形的 SignatureOffset 影响的整个过程总结

解析signArray,向13th sector写入0x0050,解析fake client context,使得14th sector写入0xffff,最后ClfsEncodeBlockPrivate,写回signArray,从而构造了一个超大值signaturesOffset

Vuln

POC 分析

重新来看这个POC的图

POC

  1. 初始化MyLog.blf
  2. 堆风水布局,以使得后面的MyLog的堆块和MyLxg_xxx的堆块是连续的
  3. 构造畸形的MyLog.blf,解析时,仍能打开HANDLE
  4. 构造与MyLog堆块连续的堆块MyLxg_xxx
  5. MyLxg_xxx添加LogContainer,在这个结构体中存在一个函数指针pContainer
  6. 获取NtSetInformationFile的地址
  7. MyLog添加LogContainer,此时已经可以溢出到MyLxg_xxxContainer ContextpContainer
  8. 通过NtSetInformationFile设置MyLxg_xxxFileDispositionInformation,即该函数将在文件关闭时删除文件或取消先前请求的删除,因此在关闭文件时,可以触发到removeContainerpContainer的使用,从而触发BSOD
  9. 触发漏洞

在扩大这个漏洞影响时,是覆盖了SignaturesOffset,而在CClfsBaseFilePersisted::AllocSymbol时,则利用SignaturesOffset做的检测

AllocSymbolIDA

因此此时,可以直接绕过这个检测,使得cbSymbolZone可以为一个大值,从而覆盖到下一个堆块的pContainer

OOBW

最后漏洞触发是在关闭blf文件时,在函数CClfsBaseFilePersisted::RemoveContainer中,会直接使用pContainer的虚表进行函数调用,而被破坏的pContainer会导致BSOD甚至EOP

patch

该漏洞的patch,在CClfsBaseFilePersisted::LoadContainerQ时,对于cbSymbolZonesignaturesOffset进行了检测

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

patch

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