首页
社区
课程
招聘
[原创]CVE-2022-37969 CLFS 提权漏洞
2023-5-26 18:34 17969

[原创]CVE-2022-37969 CLFS 提权漏洞

2023-5-26 18:34
17969

大佬可直接转至https://www.zscaler.com/blogs/security-research/technical-analysis-windows-clfs-zero-day-vulnerability-cve-2022-37969-part
照着文章复现一遍
通用日志文件系统(CLFS)是一种通用的日志记录子系统,可供运行在内核模式和用户模式下的应用程序使用,用于构建高性能的事务日志,并在驱动程序CLFS.sys中进行实现。通用日志文件系统在基本日志文件(BLF)中生成事务日志。
CLFS的官方文档:https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/introduction-to-the-common-log-file-system
CLFS的非官方文档:https://github.com/ionescu007/clfs-docs/blob/main/README.md

blf 日志文件格式

图片描述
基本日志文件由六个不同的元数据块组成,控制块(Control Block)、基本块(Base Block)和截断块(Truncate Block)以及它们对应的影子块。三种类型的记录(控制记录、基本记录和截断记录((Control Record, Base Record, and Truncate Record))可以驻留在这些块中。 Base Record包含符号表,这些符号表存储有关与基本日志文件关联的客户端上下文、容器上下文和安全上下文的信息。
每个日志块的都有一个日志块头CLFS_LOG_BLOCK_HEADER,大小为0x70字节的CLFS_LOG_BLOCK_HEADER结构的内存布局如上图所示,结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct _CLFS_LOG_BLOCK_HEADER
{    
    UCHAR MajorVersion;
    UCHAR MinorVersion;
    UCHAR Usn;
    CLFS_CLIENT_ID ClientId;
    USHORT TotalSectorCount;
    USHORT ValidSectorCount;
    ULONG Padding;
    ULONG Checksum;
    ULONG Flags;
    CLFS_LSN CurrentLsn;
    CLFS_LSN NextLsn;
    ULONG RecordOffsets[16];
    ULONG SignaturesOffset;
} CLFS_LOG_BLOCK_HEADER, *PCLFS_LOG_BLOCK_HEADER;

基本块在.BLF文件的偏移0x800到0x71FF,以Log Block Header (0x70 bytes)开始,然后是基础记录头与记录
基础记录头的的CLFS_BASE_RECORD_HEADER结构如下
图片描述
图片描述
Base Record以大小为 0x1338 字节的标头 (CLFS_BASE_RECORD_HEADER) 开始,后面是相关的上下文数据。在CLFS_BASE_RECORD_HEADER中,与该漏洞相关的一些重要字段说明如下:
rgClients表示指向客户端上下文对象的偏移数组。
rgContainers表示指向容器上下文对象的偏移数组。
cbSymbolZone表示符号区域中新符号的下一个可用偏移量。
在Base Record中 Client Context, Container Context, 和Shared Security Context 用符号表示,他们前面是CLFSHASHSYM

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
typedef struct _CLFS_NODE_ID {
   ULONG cType;
   ULONG cbNode;
 } CLFS_NODE_ID, *PCLFS_NODE_ID;
 
  typedef struct _CLFSHASHSYM
{    CLFS_NODE_ID cidNode;
     ULONG ulHash;
     ULONG cbHash;
     ULONGLONG ulBelow;
     ULONGLONG ulAbove;
     LONG cbSymName;
     LONG cbOffset;
     BOOLEAN fDeleted;
 } CLFSHASHSYM, *PCLFSHASHSYM;

CLFSHASHSYM 的内存布局如下
图片描述
在Base Record,中,客户端上下文(client context )用于标识日志文件的客户端。在基础日志文件中至少可以创建一个客户端上下文。CLFS_CLIENT_CONTEXT结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
typedef struct _CLFS_CLIENT_CONTEXT
 {
     CLFS_NODE_ID cidNode;
     CLFS_CLIENT_ID cidClient;
     USHORT fAttributes;
     ULONG cbFlushThreshold;
     ULONG cShadowSectors;
     ULONGLONG cbUndoCommitment;
     LARGE_INTEGER llCreateTime;
     LARGE_INTEGER llAccessTime;
     LARGE_INTEGER llWriteTime;
     CLFS_LSN lsnOwnerPage;
     CLFS_LSN lsnArchiveTail;
     CLFS_LSN lsnBase;
     CLFS_LSN lsnLast;
     CLFS_LSN lsnRestart;
     CLFS_LSN lsnPhysicalBase;
     CLFS_LSN lsnUnused1;
     CLFS_LSN lsnUnused2;
     CLFS_LOG_STATE eState; //+0x78
     union
     {
         HANDLE hSecurityContext;
         ULONGLONG ullAlignment;
     };
 } CLFS_CLIENT_CONTEXT, *PCLFS_CLIENT_CONTEXT;

在Base Record中,container context与为一个base log文件添加一个容器文件有关
CLFS_CONTAINER_CONTEXT结构如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
typedef struct _CLFS_CONTAINER_CONTEXT
{
     CLFS_NODE_ID cidNode; //8 bytes
     ULONGLONG cbContainer; //8 bytes
     CLFS_CONTAINER_ID cidContainer; // 4 bytes
     CLFS_CONTAINER_ID cidQueue; // 4 bytes
     union
     {
         CClfsContainer* pContainer; //8 bytes
         ULONGLONG ullAlignment;
     };
     CLFS_USN usnCurrent;
     CLFS_CONTAINER_STATE eState;
     ULONG cbPrevOffset; //4 bytes
     ULONG cbNextOffset; //4 bytes
 } CLFS_CONTAINER_CONTEXT, *PCLFS_CONTAINER_CONTEXT;

pContainer是指向运行时表示容器的CClfsContainer对象的内核指针,该指针位于CLFS_CONTAINER_CONTEXT结构的偏移0x18处
图片描述

poc编写

文章中给出了部分poc
图片描述

 

0x09a8: |68 13 00 00| => |30 1B 00 00|
把0x1386的Offset修改为了0x1B30,弃用0x1BD8=0x1386+0x870的Client Context,在0x23A0=0x1B30+0x870的地方重新伪造一个

 

0x1B98: |F8 00 00 00| => |4B 11 01 00|
cbSymbolZone 0x1114B
改大cbSymbolZone,以使得AddLogContainer时,造成堆越界写

 

poc中包含的步骤:
1 通过CreateLogFile 在C:\Users\Public\ 目录下创建日志文件MyLog.blf
2 创建数十个MyLog_xxx.blf基础日志文件,使用一个常数计数器来创建基本日志文件,因此可能不会使连续创建的两个内存区域之间的偏移量保持恒定(0x11000字节)。因此,常量值必须针对这种情况进行调整。
3 重新计算Base Block的crc32,然后将新的校验值写入偏移量0x80C处。然后打开MyLog.blf。
4 调用CreateLogFile 在文件夹C:\Users\Public\中创建一个基本日志文件MyLxg_xxx.blf。
5 调用AddLogContainer 为步骤4中创建的MyLxg_xxx.blf添加日志容器。
6 调用GetProcAddress(LoadLibraryA("ntdll.dll"), "NtSetInformationFile")来获取NtSetInformationFile的函数地址。
7 调用AddLogContainer 为步骤3中打开的MyLog.blf添加日志容器
8 调用NtSetInformationFile(v55, (PIO_STATUS_BLOCK)v33, v28, 1, (FILE_INFORMATION_CLASS)13),最后一个参数是FileInformationClass类型。当值为FileDispositionInformation(13)时,该函数将在关闭文件时删除该文件或取消之前请求的删除。
9 调用CloseHandle 关闭MyLxg_xxx.blf的句柄,触发此漏洞。
还原poc,poc见附件
运行poc,触发崩溃
图片描述

分析原因

poc中的第5和第7步分别调用AddLogContainer将容器添加到与日志句柄关联的日志。
在 CLFS.sys 中,CClfsRequest类负责处理来自用户空间的请求。CClfsRequest ::AllocContainer函数用于处理向物理日志添加容器的请求。
CClfsRequest ::AllocContainer函数调用CClfsLogFcbPhysical::AllocContainer
图片描述
CClfsLogFcbPhysical::AllocContainer声明如下:

1
__int64 __fastcall CClfsLogFcbPhysical::AllocContainer(CClfsLogFcbPhysical *this, struct _FILE_OBJECT *a2, struct _UNICODE_STRING *a3, unsigned __int64 *a4)

bu CLFS!CClfsLogFcbPhysical::AllocContainer断点被触发,rcx存放的是CClfsLogFcbPhysical类的this指针
图片描述
CClfsLogFcbPhysical类中vftable的地址存储在偏移量 0x00 处。在this+0x30 处,存储了指向日志名称的指针
this +0x2B0 存储了指向CClfsBaseFilePersisted类的指针
AddLogContainer fail的情况下这里为0(win10 this +0x2B0指向的是自己(this +0x2B0),找不到CLFS!CClfsBaseFilePersisted::vftable)
图片描述
win11
在内存中 一个CLFS基本日志文件可以由CClfsBaseFile类表示,可由CClfsBaseFilePersisted 类拓展,CClfsBaseFilePersisted的指针+0x30处储存了一个指向0x90字节大小的堆的指针,
图片描述
在CClfsBaseFilePersisted的this指针偏移 0x1C0 处,存储了指向CClfsContainer对象的指针,该指针来自CLFS_CONTAINER_CONTEXT 结构中的字段pContainer。添加容器成功后可看到
图片描述
此时可以在CLFS_CONTAINER_CONTEXT+0x18处设置内存写断点,跟踪CLFS_CONTAINER_CONTEXT结构中CClfsContainer对象的指针何时损坏。CClfsBaseFilePersisted 对象中偏移量 0x1C0 处的另一个内存写入断点可以设置如下:
1: kd> ba w8 ffffe689 255374f0 //CLFS_CONTAINER_CONTEXT+0x18
1: kd> ba w8 ffffe689 25159b20 //CClfsBaseFilePersisted+0x1C0
到第七步给Mylog AllocContainer时
图片描述
图片描述
最初在 MyLog.blf 文件中设置为 0x00000050 的 SignaturesOffset 字段在内存中已设置为 0xFFFF0050
图片描述
CLFS_CONTAINER_CONTEXT 结构中偏移量 0x18 处的CClfsContainer指针损坏。FFFF E689 2514 9B20==》30c1fdf0`06159b20
继续运行代码。将命中内存写入断点CLFS_CONTAINER_CONTEXT: +0x18
图片描述
CClfsBaseFilePersisted::AllocSymbol函数中调用memset函数触发内存写断点,Base Record 中的CLFS_CONTAINER_CONTEXT产生越界写入,这会导致CClfsContainer对象中的指针损坏
图片描述
图片描述
根据栈回溯,CClfsBaseFilePersisted::AllocSymbol函数中调用memset函数触发内存写断点。
图片描述
下图展示越界写入是如何发生的,以及如何导致CClfsContainer对象中的指针损坏
图片描述
当在用户空间调用CloseHandle函数时, CClfsRequest::Close(PIRP Irp)处理这个请求。在内核中,另一个内存断点 (0x1c0+CClfsBaseFilePersisted) 在ClfsBaseFilePersisted::WriteMetadataBlock函数中命中
图片描述
从CLFS_CONTAINER_CONTEXT中的CClfsContainer将损坏的指针复制到Base Record中CClfsBaseFilePersisted+0x1c0处
图片描述
最后,在CLFS!CClfsBaseFilePersisted::RemoveContainer中取消引用指向CClfsContainter对象的损坏指针会导致内存冲突,造成崩溃
图片描述
CClfsBaseFilePersisted::RemoveContainer函数伪代码:
图片描述
图片描述
CClfsBaseFilePersisted::RemoveContainer函数执行有以下步骤。
1.获取 Base Record 中偏移 0x398 处的container context的偏移量(0x1468)。
2.调用CClfsBaseFile::GetSymbol获取指向 CLFS_CONTAINER_CONTEXT 结构的指针并将其存储在第 4 个参数中。
3.将步骤2中获取的CLFS_CONTAINER_CONTEXT结构中指向CClfsContainter对象的指针的值赋给局部变量v13。
4.将CLFS_CONTAINER_CONTEXT 结构中指向CClfsContainter对象的指针清零。
5.依次调用CClfsContainer::Remove和CClfsContainer::Release删除关联的容器日志文件并释放CClfsContainer对象。

补丁

补丁补在了CClfsBaseFilePersisted::LoadContainerQ中,如下
图片描述
当用户空间调用CreateLogFile函数时,CLFS!CClfsRequest::Create负责处理这个请求。当 CreateLogFile函数用于打开现有的基本日志文件时,会调用CClfsLogFcbPhysical::Initialize
图片描述
CClfsLogFcbPhysical::Initialize调用CClfsBaseFilePersisted::LoadContainerQ
图片描述

SignaturesOffset被覆盖的过程

CClfsLogFcbPhysical::Initialize中的5步
1.调用CClfsBaseFilePersisted::OpenImage函数为基础日志文件中的基础块创建一个bigpool(大小:0x7a00)
图片描述
OpenImage最终会调用ReadMetadataBlock
CClfsBaseFilePersisted::OpenImage -> CClfsBaseFilePersisted::ReadImage -> CClfsBaseFile::AcquireMetadataBlock -> CClfsBaseFilePersisted::ReadMetadataBlock
图片描述
2.调用CClfsBaseFile::AcquireClientContext函数从基块获取客户端上下文
图片描述
对.blf文件的修改
图片描述
3.检查eState字段是否为CLFS_LOG_SHUTDOWN
图片描述
4.调用CClfsLogFcbPhysical::ResetLog函数
图片描述
图片描述
(文中的原图)
图片描述
5.调用CClfsLogFcbPhysical::FlushMetaData函数
图片描述
图片描述
ClfsEncodeBlockPrivate从基块中的每个扇区获取扇区签名,并用扇区签名覆盖扇区签名数组,扇区签名数组位于偏移 0x50 处,与基块中的 SignaturesOffset 字段重叠。第 14 个扇区的扇区签名已设置为 0xFFFF, 在基本块中的偏移量 0x6C (0x50+0xE*2) 处的两个字节被覆盖(0xFFFF)。SignaturesOffset 字段的值为 0xFFFF0050
(文中的原图)
图片描述
覆盖 SignaturesOffset 字段的过程
图片描述
参考链接:https://www.zscaler.com/blogs/security-research/technical-analysis-windows-clfs-zero-day-vulnerability-cve-2022-37969-part
https://www.coresecurity.com/core-labs/articles/understanding-cve-2022-37969-windows-clfs-lpe
https://paper.seebug.org/1920
https://bbs.kanxue.com/thread-275566.htm#msg_header_h2_4


[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

最后于 2023-6-7 11:16 被hml189编辑 ,原因: 增加内容
收藏
点赞3
打赏
分享
最新回复 (2)
雪    币: 19103
活跃值: (28707)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-10-16 09:08
3
1
感谢分享
游客
登录 | 注册 方可回帖
返回