发布于周五 15:145天前 本文是对之前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 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 函数。 在CClfsBaseFilePersisted:RemoveContainer函数中,还调用了CClfsBaseFilePersisted:FlushImage函数,然后在获取到值并验证后,调用pContainer对象中存储的函数。 根据CClfsContainer:CClfsContainer函数,*pContainer存储在虚函数表中。 此时我们正在查看CClfsBaseFilePersisted:FlushImage函数 CClfsBaseFilePersisted:WriteMetadataBlock 函数将在内部执行。该函数会遍历每个容器上下文,先保存pContainer,然后将其设置为0。 然后它内部调用ClfsEncodeBlock和ClfsDecodeBlock对数据进行编码和解码。直观感受如图 虽然在上述过程中代码刻意保护了pContainer并将其设置为0,但是在ClfsEncodeBlock编码过程中,每0x200字节的最后两个字节都会被写入到SignaturesOffset中。那么如果修改_CLFS_LOG_BLOCK_HEADER中的SignaturesOffset字段,使其与_CLFS_CONTAINER_CONTEXT中的pContainer指针相交,那么在函数编码时,写入的内容将覆盖pContainer执行。这样,当CClfsBaseFilePersisted:FlushImage函数调用pContainer时,实际上执行的是我们构造的地址。这就导致RIP达到了函数执行任意函数的目的。 ### CVE-2022-30220 该漏洞位于CClfsBaseFilePersisted:ReadImage函数 它没有上一个漏洞较多的繁琐流程,而且这个漏洞通过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。 终于被执行了。使用ClfsEncodeBlockPrivate函数时,blockHeader指向一个未初始化的空间,然后在写入下面的数据进行操作时,发送错误,导致崩溃。 ### CVE-2022-37969 该漏洞的主要问题是对clf.sys中基本日志文件(BLF)的基本记录头中的cbSymbolZone字段缺乏严格的边界检查。在上面前言的介绍中,我们知道BLF文件是在0x200字节扇区中写入/读取的,最后两个字节用于存储扇区签名。对于合法的BLF 文件,包含原始字节的数组位于块的末尾。 ClfsEncodeBlock 和ClfsDecodeBlock 函数负责编码、解码并将签名和原始字节写入各自的位置。 (函数处理逻辑我们在24521中已经分析过)。另请注意,_CLFS_BASE_RECORD_HEADER 结构中有一个cbSymbolZone。经过查看,我找到了关键函数CClfsBaseFilePersisted:AllocSymbol 从函数中可以看出,修改了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 函数检查中断的块扩展操作是否应继续。 该函数检查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函数中调用CClfsBaseFilePersisted:ReadImage函数 然后在CClfsBaseFilePersisted:ReadImage函数中调用CClfsBaseFile:GetControlRecord 在CClfsBaseFilePersisted:OpenImage函数中,函数调用的逻辑顺序是ReadImage-GetBaseLogRecord-条件判断(分别判断eExtendState iExtendBlock iFlushBlock的值)-ExtendMetadataBlock。 CClfsBaseFilePersisted:ExtendMetadataBlock函数会依赖iExtendBlock的值来加载元数据,然后获取指针,并使用eExtendState的值进行分支判断。 然后调用CClfsBaseFilePersisted:FlushControlRecord函数 CClfsBaseFilePersisted:FlushControlRecord函数对GetControlRecord获得的日志控制块进行分支判断,然后可以转到CClfsBaseFilePersisted:WriteMetadataBlock函数。然后在WriteMetadataBlock函数中获取并更新元数据块的地址,更新后的条件参数就是iFlushBlock的值。内部调用ClfsEncodeBlock和WriteSector来编码和写入数据。 在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 添加一名作者
创建帐户或登录后发表意见