跳转到帖子

游客您好,欢迎来到黑客世界论坛!您可以在这里进行注册。

赤队小组-代号1949(原CHT攻防小组)在这个瞬息万变的网络时代,我们保持初心,创造最好的社区来共同交流网络技术。您可以在论坛获取黑客攻防技巧与知识,您也可以加入我们的Telegram交流群 共同实时探讨交流。论坛禁止各种广告,请注册用户查看我们的使用与隐私策略,谢谢您的配合。小组成员可以获取论坛隐藏内容!

TheHackerWorld官方

Windows之clfs日志提权漏洞

精选回复

发布于

本文是对之前Windows-clfs漏洞的梳理,不涉及后续的利用,因为CLFS驱动在短时间内暴露了很多漏洞,并且很多漏洞涉及的函数和clfs结构都是相关的。还有不同的方法可以绕过之前的CVE 补丁。所以我想好好总结一下,整理一下,让自己能够从中学习到思路,分析一下绕过它的心路历程。

关于clfs漏洞,在梳理之前,需要了解以下几点,这也是每一篇clfs系列文章中的常识性知识。

# 必备知识

首先,通用日志文件系统(CLFS) API 提供了一个高性能、通用的日志文件子系统,可供专用客户端应用程序使用并由多个客户端共享,以优化日志访问。这是微软对CLFS的解释,微软还提供了一系列的API来对日志进行操作、追加、访问等操作。这些API在后续的漏洞利用过程中也是不可或缺的。

例如:

````

CLFSUSER_API HANDLE CreateLogFile(

[输入] LPCWSTR pszLogFileName,

[输入] ACCESS_MASK fDesiredAccess,

[输入] DWORD dwShareMode,

[输入,可选] LPSECURITY_ATTRIBUTES psaLogFile,

[在] ULONG fCreateDisposition,

[输入] ULONG fFlagsAndAttributes

);

CreateLogFile(日志文件名、访问权限[读或写和删除]、文件共享模式、指向SECURITY_ATTRIBUTES 结构的指针、打开/创建新文件、文件属性和标志)

CreateLogFile(logFileName.c_str(), GENERIC_WRITE | GENERIC_READ, 0, NULL, OPEN_ALWAYS, 0);

````

任何需要日志记录或恢复支持的用户模式应用程序都可以使用CLFS。 CLFS数据结构大致可以分为三种:

1. 存储在内核模式内存中并附加到一个对象(例如附加到File 对象的FCB)。 2. 暂时保存在用户态或内核态调用者的内存中。 3、持久化存储在磁盘上的基本日志文件(BLF),即用户可以通过CreateLogFile函数创建一个日志文件,然后在本地创建一个同名、后缀为.blf的日志文件,其中包含日志存储所需的一些信息。

其次,LOG_BLOCK:每个日志块都以名为CLFS_LOG_BLOCK_HEADER 的块头开始。

````

typedef 结构_CLFS_LOG_BLOCK_HEADER

{

UCHAR 主要版本;

UCHAR 次要版本;

UCHAR 乌斯恩;

CLFS_CLIENT_ID 客户端ID;

USHORT 总扇区计数;

USHORT 有效扇区计数;

乌龙填料;

乌龙校验和;

乌龙旗;

CLFS_LSN 当前Lsn;

CLFS_LSN 下一个Lsn;

ULONG 记录偏移量[16];

ULONG 签名偏移;

} CLFS_LOG_BLOCK_HEADER, *PCLFS_LOG_BLOCK_HEADER;

by:https://github.com/ionescu007/clfs-docs/

````

其中RecordOffsets是日志块中记录的偏移量数组,CLFS只处理指向CLFS_LOG_BLOCK_HEADER末尾的第一条记录的偏移量(0x70)。

````

typedef 结构_CLFS_LOG_BLOCK_HEADER

{

UCHAR 扇区_块_类型;

UCHAR 乌斯恩;

};

````

当日志文件存储在磁盘上时,其日志块将被编码。在编码过程中,还有一个SignaturesOffset字段指向的数组。编码时,每个扇区都有一个签名以确保一致性。或者Checksum是日志块数据的校验和,用于验证读取的数据。使用CRC32 校验和。函数对应:CCrc32:ComputeCrc32 b9286552b35f12102ea3fd81aa84b5c7.png

Base Record定义如下,它存储用于将基本日志文件与容器关联的元数据

````

typedef 结构_CLFS_BASE_RECORD_HEADER

{

CLFS_METADATA_RECORD_HEADER hdrBaseRecord;

CLFS_LOG_ID cidLog;

ULONGLONG rgClientSymTbl[CLIENT_SYMTBL_SIZE];

ULONGLONG rgContainerSymTbl[CONTAINER_SYMTBL_SIZE];

ULONGLONG rgSecuritySymTbl[SHARED_SECURITY_SYMTBL_SIZE];

ULONG cNextContainer;

CLFS_CLIENT_ID cNextClient;

ULONG cFreeContainers;

ULONG cActiveContainers;

ULONG cbFreeContainers;

ULONG cbBusyContainers;

ULONG rgClients[MAX_CLIENTS_DEFAULT];

ULONG rgContainers[MAX_CONTAINERS_DEFAULT];

ULONG cbSymbolZone;

乌龙cb扇区;

USHORT b 未使用;

CLFS_LOG_STATE 日志状态;

UCHAR cUn;

UCHAR cClients;

} CLFS_BASE_RECORD_HEADER, *PCLFS_BASE_RECORD_HEADER;

````

然后cActiveContainers保存当前活动容器的数量,rgContainers数组保存容器上下文的偏移值。用户可以使用AddLogContainer 和AddLogContainerSet 函数将容器添加到日志中。容器上下文由以下结构体表示:

typedef 结构_CLFS_CONTAINER_CONTEXT

````

typedef 结构_CLFS_CONTAINER_CONTEXT

{

CLFS_NODE_ID cidNode;

ULONGLONG cbContainer;

CLFS_CONTAINER_ID cidContainer;

CLFS_CONTAINER_ID cidQueue;

联盟

{

CClfsContainer* pContainer;

ULONGLONG uAlAlignment;

};

CLFS_USN usn当前;

CLFS_CONTAINER_STATE eState;

ULONG cbPrevOffset;

ULONG cbNextOffset;

} CLFS_CONTAINER_CONTEXT, *PCLFS_CONTAINER_CONTEXT;

* pContainer实际上包含一个指向CClfsContainer的内核指针,在运行时描述容器

* 当日志文件位于磁盘上时,该字段必须设置为零。

````

BLF 文件也是生成的日志文件。组成blf的不同元数据对应不同类型的数据。注意控制块,它包含有关布局、扩展区域和截断区域的信息。其结构如下,其中rgBlocks保存的是日志块的大小。

````

typedef 结构_CLFS_CONTROL_RECORD

{

CLFS_METADATA_RECORD_HEADER hdrControlRecord;

ULONGLONG ullMagicValue;

乌龙版;

CLFS_EXTEND_STATE eExtendState;

USHORT iExtendBlock;

USHORT iFlushBlock;

ULONG cNewBlockSectors;

ULONG cExtendStartSectors;

ULONG cExtendSectors;

CLFS_TRUNCATE_CONTEXT cxTruncate;

ULONG cBlocks;

ULONG c保留;

CLFS_METADATA_BLOCK rgBlocks[6];

CLFS_CONTROL_RECORD;

````

CLFS_METADATA_BLOCK的结构是

````

typedef 结构_CLFS_METADATA_BLOCK

{

乌龙龙pbImage;

乌龙cbImage;

ULONG cbOffset;

CLFS_METADATA_BLOCK_TYPE eBlockType;

乌龙填料;

CLFS_METADATA_BLOCK;

````

上述CLFS数据结构,以及对日志文件进行编码、添加容器等一些操作,在后续的CVE漏洞分析中将会提到。是下面漏洞分析的关键。更好地梳理clfs中的不同数据结构,了解它们的作用和功能,可以更好地帮助复现和发现新clfs驱动系列中的问题。

## 漏洞分析

### CVE-2022-24521

漏洞原因:CClfsBaseFilePersisted:LoadContainerQ 该函数中有一个逻辑。我们就正常整理一下吧。如果CLFS_CONTAINER_CONTEXT-cidQueue 的值为-1 ,那么此时CLFS_CONTAINER_CONTEXT-pContainer 的值被设置为0,然后会调用CClfsBaseFilePersisted:RemoveContainer 函数。f392ec37a142489a1e34d1f8efd1a38a.png

在CClfsBaseFilePersisted:RemoveContainer函数中,还调用了CClfsBaseFilePersisted:FlushImage函数,然后在获取到值并验证后,调用pContainer对象中存储的函数。96b9211150a7c9d0bb89264c5aef68ec.png

根据CClfsContainer:CClfsContainer函数,*pContainer存储在虚函数表中。1de250102d0e84df4297fa21343a8a5b.png

此时我们正在查看CClfsBaseFilePersisted:FlushImage函数c3346c27b8ca8f2130b7809aa1d29068.png

CClfsBaseFilePersisted:WriteMetadataBlock 函数将在内部执行。该函数会遍历每个容器上下文,先保存pContainer,然后将其设置为0。5aa1d981252bb9dffb155ae7ecffa5b6.png

然后它内部调用ClfsEncodeBlock和ClfsDecodeBlock对数据进行编码和解码。直观感受如图fff8799e74e24768c77accef8cbf50c0.png

虽然在上述过程中代码刻意保护了pContainer并将其设置为0,但是在ClfsEncodeBlock编码过程中,每0x200字节的最后两个字节都会被写入到SignaturesOffset中。那么如果修改_CLFS_LOG_BLOCK_HEADER中的SignaturesOffset字段,使其与_CLFS_CONTAINER_CONTEXT中的pContainer指针相交,那么在函数编码时,写入的内容将覆盖pContainer执行。这样,当CClfsBaseFilePersisted:FlushImage函数调用pContainer时,实际上执行的是我们构造的地址。这就导致RIP达到了函数执行任意函数的目的。

### CVE-2022-30220

该漏洞位于CClfsBaseFilePersisted:ReadImage函数69cc1604173033bf9c8f9dab7758022f.png

它没有上一个漏洞较多的繁琐流程,而且这个漏洞通过patch diff的效果也更直观。主要是关于_CLFS_CONTROL_RECORD-cBlocks(cBlocks字段表示数组中块的数量)值大小的验证。如上图所示,if(cBlocks 6u)通过了这个判断,然后在函数中使用ExAllocatePoolWithTag分配两个池,池空间大小分别为24和2。然后对两个池执行memmove操作。然后将_CLFS_CONTROL_RECORD - rgBlocks 列表复制到pool1。

其实当我看到这里的分配时,我就有了一个猜测,是不是分配了一个大小为0的地址。即未初始化的空间。看完日志块的函数,继续往下看CClfsBaseFileSnapshot:InitializeSnapshot函数,该函数也是使用ExAllocatePoolWithTag函数来分配。此时cblocks的值比实际值要大。这样导致ExAllocatePoolWithTag最终分配的空间为0,那么下面的memmove函数复制的数据也为0。0b852fb22116cdee6e55520fe0137c59.png

终于被执行了。使用ClfsEncodeBlockPrivate函数时,blockHeader指向一个未初始化的空间,然后在写入下面的数据进行操作时,发送错误,导致崩溃。

### CVE-2022-37969

该漏洞的主要问题是对clf.sys中基本日志文件(BLF)的基本记录头中的cbSymbolZone字段缺乏严格的边界检查。在上面前言的介绍中,我们知道BLF文件是在0x200字节扇区中写入/读取的,最后两个字节用于存储扇区签名。对于合法的BLF 文件,包含原始字节的数组位于块的末尾。 ClfsEncodeBlock 和ClfsDecodeBlock 函数负责编码、解码并将签名和原始字节写入各自的位置。 (函数处理逻辑我们在24521中已经分析过)。另请注意,_CLFS_BASE_RECORD_HEADER 结构中有一个cbSymbolZone。经过查看,我找到了关键函数CClfsBaseFilePersisted:AllocSymbol bb2c4e4a10753b6b0391e4c64b8af763.png

从函数中可以看出,修改了cbSymbolZone的值,然后执行了if判断。 cbSymbolZone主要用于计算新添加的Container应该跳过多少空间,这也是这里漏洞的关键。该函数直接使用blf文件中存储的cbSymbolZone的值,这样我们如果操作cbSymbolZone的值,就可以重写容器,修改容器的pcontainer指针。

函数中的if判断是我们下一步需要考虑的,但与cbSymbolZone比较的值是SignaturesOffset字段。这个字段可能会被我们创建的blf文件中内存中的大量数据覆盖。这样,即使cbSymbolZone字段设置为异常值,仍然可以绕过cbSymbolZone字段的验证。

这样,当函数调用memset()时,就会导致v10处发生越界写入。该偏移量属于日志中的CLFS_CONTAINER_CONTEXT 结构。这样,如果CLFS_CONTAINER_CONTEXT结构中偏移处的CClfsContainer指针损坏,它可以指向可以申请的用户态内存地址,然后申请内存覆盖其地址。

### CVE-2023-23376

该漏洞主要涉及CLFS_CONTROL_RECORD结构体。此结构用于保存CLFS_METADATA_BLOCK,其中包含有关日志中存在的所有块的信息,并且还具有用于更改块大小的附加字段。 block与eExtendState相关,iFlushBlock——正在写入的block的索引; cNewBlockSectors - 新块的大小(以扇区为单位); cExtendStartSectors - 原始块大小; cExtendSectors - 添加的扇区数。 cclfsbasefilepersist:OpenImage 函数检查中断的块扩展操作是否应继续。2445f332057c5c9ab5faf981c1aac081.png

该函数检查iExtendBlock和iFlushBlock索引,它们应该小于6。否则,块指针将在CClfsBaseFilePersisted:ExtendMetadataBlock函数中的映射缓冲区m_rgBlocks之外读取。

不检查传入CClfsBaseFilePersisted:ExtendMetadataBlock 函数的iExtendBlock 和iFlushBlock。

所以这里有一个问题。 OpenImage函数检查,而其他函数调用ExtendMetadataBlock函数时,出现问题,

但如果函数调用过程首先由OpenImage函数检查,那么索引检查就会失败。但是,如果您在该函数检查后修改了CLFS_CONTROL_RECORD 或索引,则可以使用它来将地址作为指针传递。

这也是Windows中的一个经典逻辑漏洞。

因此,该漏洞最终改变了CLFS_CONTROL_RECORD结构中的eExtendState、iExtendBlock、iFlushBlock等字段的值。

然后在OpenImage中执行ExtendMetadataBlock函数时,修改CLFS_CONTROL_RECORD以递增CLFS_CONTROL_RECORD-DumpCount,以便ClfsEncodeBlock函数(在24521中引入)对该块进行编码,然后写入其更改的数据。这将覆盖CLFS_LOG_BLOCK_HEADER-RecordOffsets[0]。最后修改的CLFS_CONTROL_RECORD将被执行,任何值都可以在内存中执行。

### CVE-2023-28252

首先在CClfsBaseFilePersisted:OpenImage函数35dc28d8f8863b3d6cf91dedc81223bb.png中调用CClfsBaseFilePersisted:ReadImage函数

然后在CClfsBaseFilePersisted:ReadImage函数中调用CClfsBaseFile:GetControlRecord b2d67a799a7b8cde67ff6575472b0b30.png

在CClfsBaseFilePersisted:OpenImage函数中,函数调用的逻辑顺序是ReadImage-GetBaseLogRecord-条件判断(分别判断eExtendState iExtendBlock iFlushBlock的值)-ExtendMetadataBlock。0365c45b0920a57550c218374039736a.png

CClfsBaseFilePersisted:ExtendMetadataBlock函数会依赖iExtendBlock的值来加载元数据,然后获取指针,并使用eExtendState的值进行分支判断。4fdebc6457173cb888848b91bc03c91d.png

然后调用CClfsBaseFilePersisted:FlushControlRecord函数166caf4fb8b1aabae270937e43d4e1fb.png

CClfsBaseFilePersisted:FlushControlRecord函数对GetControlRecord获得的日志控制块进行分支判断,然后可以转到CClfsBaseFilePersisted:WriteMetadataBlock函数。然后在WriteMetadataBlock函数中获取并更新元数据块的地址,更新后的条件参数就是iFlushBlock的值。内部调用ClfsEncodeBlock和WriteSector来编码和写入数据。366c07ca536ca9e781f85f7b43e6cbf9.png

在WriteMetadataBlock函数中,直接调用WriteSector函数来写入数据。假设执行ClfsEncodeBlock时,首先将校验和设置为0,然后ClfsEncodeBlockPrivate检测到异常并返回错误代码,因此ClfsEncodeBlock不会更新校验和。那么元数据块的校验和就变成0,从而绕过OpenImage函数中的检测。

CreateLogFile函数将调用CClfsBaseFilePersisted:CheckSecureAccess函数。 CheckSecureAccess函数会遍历容器获取pContainer值并调用虚函数。因此,可以创建一个包含精心构造的容器的日志文件,然后利用该漏洞修改该容器的偏移量,使得再次调用CreateLogFile时,遍历到的容器就是构造好的容器,然后pContainer值指向在用户空间构造的虚函数布局,这样当调用该虚函数时,就可以执行我们要执行的函数了。

总结一下:当WriteMetadataBlock的ClfsEncodeBlock检测到异常并触发错误代码时,它仍然会更新MetaBlockControl块数据,这会导致下次加载MetaBlockControl块时对MetaBlockControlShadow块的越界访问。

通过分析多个clfs漏洞,我们可以发现这些漏洞基本集中在日志文件的写入操作上。每个块头的处理还包括偏移量和指针的操作。

## 总结

在整理clfs系列的漏洞时,我觉得最好通过diff的结果来分析,然后结合细节进行分析,因为clfs系列的漏洞利用了很多相关的功能。

整个clfs漏洞系列与blf文件内部的结构、将blf文件写入日志系统时调用的函数以及用于存储实际数据的容器有关。而对于CLFS_LOG_BLOCK_HEADER等,大多数驱动都会从这里开始,通过偏移量来分析并完成该过程。因此,修改偏移量等位置信息造成内存损坏是漏洞发现的关键点。还有涉及比较多的CLFS_CONTAINER_CONTEXT等等。

参考:

https://bbs.kanxue.com/thread-275566.htm#%E6%BC%8F%E6%B4%9E%E5%A4%8D%E7%8E%B0

https://github.com/ionescu007/clfs-docs/

https://mp.weixin.qq.com/s/mkLjAyalo55PmdLbzMnX9g

转载自先知社区:https://xz.aliyun.com/t/13219?time__1311=mqmxnDBD9AYew405DIAqCueWTRQIKKC4Dalichlgref=https%3A%2F%2Fxz.aliyun.com%2Ftab%2F1

添加一名作者

创建帐户或登录后发表意见

最近浏览 0

  • 没有会员查看此页面。