理解 CFBF 和 OLE 结构方便日 CVE-2022-30190
英文原文:https://danusminimus.github.io/2022/06/18/Understanding-OLE-Objects-and-Microsoft-Magic-to-mess-with-with-CVE-2022-30190(Follina).html我最初开始这项研究是为了生成提供 CVE-2022-30190 (Follina) 漏洞利用的 RTF 文件,为什么是 RTF 文件?因为RTF 文件里的 payload 将 (可能) 在所有 Windows 版本上适应 (截至撰写本报告之日),并且只需启用预览窗格并从文件资源管理器查看 RTF 文档即可执行。相反,当从 DOCX 文件加载时,payload 不会在所有 Windows 版本上执行。
为了生成包含漏洞利用的 RTF 文件,首先使用 Cas Van Cootens POC 代码生成 DOCX 文件,然后以 RTF 格式创建同一文档的新副本,这将创建一个带有漏洞利用的有效 RTF 文件。那有什么问题呢? 问题是每次想生成一个有效的 RTF 文件时,我都必须先生成一个 DOCX 文件,然后再从 Microsoft Word 中重新生成一个 RTF 文件。如果没有 MS Word 怎么办?如果我很懒怎么办?好吧,我心想自动化这件事有多难?打开 RTF 文件,看到 payload 在平面文本中保存的位置,替换它,然后我们开始了,它应该可以正常工作吗?(不是)。
如果我们只是简单地看看如何使用恶意超链接加载 RTF 文件,可以按照本文关于 CVE-2017-0199 的规定,可以看到如下:
这是正确的,只要最后一个字段 - objdata - 加载了正确的 OLE 对象。老实说,我最初对这个实现的这个假设是由于我的懒惰和一厢情愿的想法 (实际上假设在 Windows 中实现这个很容易而且不复杂)。由于我没有正确测试POC,这个错误被我完全忽略了,一个非常有创意的用户在 github 上为 Cas 的 POC 提出了一个问题,这个问题是关于我贡献的 RTF 生成功能提出的。
MSisfuckedupmanimaginepayingtogetRCEd 写了以下内容:
事实上,在进一步检查后,Cas 证实了
显然 (现在确实很有意义),当重新生成包含指向远程模板的超链接的 RTF 文件时,Office 会生成一个 OLE 对象 (实际上只需要这两个对象中的一个生成 2 个对象就可以) 这可以通过查看 HxD 中的 OLE 对象确认,我们可以在这个十六进制 blob 中看到复合文件签名,出现在用 oletools 将 RTF 文件中的 OLE 对象 dump 出来的内容中。
这促使我学习如何 dump OLE 对象并了解它们是如何工作的,以便我可以自动创建它们。有人可能会问我为什么要这样做?由于生成一个加载了 Follina 的 RTF 很容易,只需用 word 重新生成它?嗯 …… 我对 Github 问题感到恼火,只是好奇。无论如何,系好安全带并服用理智药,因为我们即将深入研究一些 Microsoft RFC 和规范!
OLE、CFB 文件格式、COM 和 Windows 理论
首先让我们查看一下 RTF 文件规范,以了解 RTF 如何存储嵌入式对象,例如文件、超链接和其他数据流。
Microsoft OLE 链接、Microsoft OLE 嵌入对象和 Macintosh Edition Manager 订阅者对象在 RTF 中表示为对象,对象是包含数据部分和结果部分的最终表示体,数据部分通常对生成文档的应用程序是隐藏的,一个单独的应用程序使用数据并提供数据的表现,这种表现是对象的结果部分。
我们可以看到这是如何使用 Word 生成的 RTF 文件实现的,该文件应包含 Follina Payload:
感兴趣的字段是 \objautlink,它指定了一个自动链接对象,它本质上是一个自动执行的 word 文档中的链接。根据 RTF 规范 \objupdate 应该强制执行它,但根据我自己的测试,这可以任意方式工作。最后,最有趣的字段是 \objdata。
此子目标包含适当格式的对象数据,OLE 对象采用 OLESaveToStream 格式,这是一个目的地控制字。
这是事情开始变得有点复杂的地方,生成的 payload 存储在 RTF 文件中的 OLE 对象中,它是一个十六进制编码的对象,它看起来像下面这样
要了解所有这些十六进制数字的含义,我们必须首先了解什么是 OLE 对象以及它们存储的格式。为此,我们将使用维基百科
OLE 允许编辑应用程序将文档的一部分导出到另一个编辑应用程序,然后将其与其他内容一起导入。例如,桌面发布系统可能会使用 OLE 将一些文本发送到文字处理器或将图片发送到位图编辑器。OLE 的主要好处是从不同的应用程序 (如文本编辑器和图像编辑器) 向文档添加不同类型的数据。这将创建一个 (https://en.wikipedia.org/wiki/Compound_File_Binary_Format) 文档和该文档引用的主文件。对主文件中数据的更改会立即影响引用它的文档。这称为 "链接" (而不是 "嵌入")。
OLE 对象本质上允许在应用程序中使用文件资源管理器插件、拖放功能、链接到 word 文档中的 excel 文档或将 GIFS 添加到电子邮件中。OLE 使用基于 FAT 文件系统规范的 CFBF 格式 (CFBF 也称为 CBF 或 CFB) 进行存储。是的,如果这听起来很疯狂,OLE 对象使用组件对象模型 (COM)。
COM 是一个二进制接口,它是许多 Microsoft 技术的基础,它允许进程间通信,从而允许在创建它们的不同环境中实现 Windows 对象。例如,Word 和 Excel 文档是不相关的,但使用 COM,我可以将可用的 Excel 文档文件链接或嵌入到 Word 文档中。COM 技术知道如何使用它的各种接口来做到这一点。
例如,如果我想在 Word 文档中嵌入一个 excel 文件并将其显示给任何人,我会在 Word 文档中嵌入一个 OLE 对象,该对象将链接或嵌入一个 excel 文件。此 OLE 对象将包含使用 Word 进程的 COM 接口编写的 "指令",该指令将解释如何加载此 excel 文件。Word 将处理 OLE 对象和 COM "指令",然后 Word 将调用指定的 COM 接口并将 excel 文档正确加载到 Word 中。Word 不了解 Excel 是什么,但使用 COM 它不需要,因为 COM 接口处理所有繁重的任务,并允许 Word 轻松链接到引用的 Excel 文档或在其中嵌入 Excel 文档。
只是写这个会伤害我的大脑,但总而言之,下面这张图片应该可以解释一切:
上手实践 OLE 和 CFBF
让我们看一下前面提到的生成的 RTF 文件中的 OLE 文件,我将存储在其中的原始对象数据加载到十六进制编辑器 HxD 中。
由于它很难阅读,我创建了一个很好阅读的图表,应该解释发生了什么 (重要的是要提到所有结构都以小端格式存储),前 33 个字节指定 OLE 对象标头 (图片中缺少最后 2 个字节)。
使用 MS-OLEDS 规范,可以解释这是一个 OLE 嵌入式对象容器,因为 FormatID 字段包含值 0x2。
类名字段也很有趣,它包含名称 "OLE2Link",这可能会给我们一些关于这个 OLE 对象的用途的提示。最后,在 ObjectHeader 之后,我们得到包含在偏移量 0x1D 中的值,即 0x0000A000,它表示该对象的整个流大小。此值非常重要,因为修改 OLE 对象也需要更改此值,否则 Word 进程不会完整读取 OLE 对象。
紧随其后的是 NativeData,该数据实际上是一种 CBFF,它存储OLE 对象、嵌入式文件或文档、链接和图片。这可以通过在 NativeData 流中找到的前 8 个字节来确认。
根据 MS-CFB,此值是 CFB 文件签名。
什么是 CBF?维基百科上解释:
复合文件二进制格式 (CFBF),也称为复合文件、复合文档格式或复合文档文件 V2 (CDF),是一种复合文档文件格式,用于在磁盘上的单个文件中存储大量文件和流。CFBF 由 Microsoft 开发,是 Microsoft COM 结构化存储的实现。最简单的复合文件二进制格式是一个容器,对其中可以存储的内容几乎没有限制。CFBF 文件结构松散地类似于 FAT 文件系统。文件被划分为扇区,这些扇区与包含与每个文件相关的扇区链的文件分配表 (不要误认为同名文件系统) 链接在一起,目录保存具有扇区 ID 的包含文件的信息 (SID) 为链的起始扇区,依此类推。
Microsoft 将 OLE 对象存储在 CBF 中,并将 COM 对象存储在这些 OLE 中。为什么微软选择 CBFs 作为存储这些对象的主要格式可以在这里阅读。 这种格式主要被 Office Open XML 取代,但正如我所见,它仍在 RTF 对象和旧的 Office 扩展中使用,例如:
[*]DOC
[*]XLS
[*]PPT
[*]POT
[*]XLT
无论如何,RTF 超链接应该存储在这个 CBF 文件中的某个位置,所以让我们遍历它。
关于 CBF 的一般准则
CBF 文件分为 512 字节大小的扇区,下面我添加了两个表格,有助于理解 CBF 文件的第一个扇区的表现 (在此处阅读)。对于 OLE 对象最感兴趣的是目录扇区,目录扇区包含有关 OLE 数据对象流的信息。
在偏移量 0x30 处,可以找到 DWORD 0x1000000000,它指示目录扇区的位置。由于 CBF 以 little endian 格式存储,因此起始位置为 1。为了计算起始位置的偏移量,我们遵循 (1+DirectoryStartingSectorLocation)*512 的公式,它使我们在偏移量 0x400 处下降。
虽然 CLSID 字段很有趣,因为它指示与文档的激活相关的 COM 对象的类型 (在本例中是 SAX XML Reader 6.0),更有趣的字段位于偏移量 0x74 和 0x78。OLE 流的起始位置是根据 Mini 流扇区计算的,在我们的例子中,该扇区从偏移量 0x600 开始,使用公式 (SSL*0x64),可以使用 oletools 集合中的 olebrowse 工具正确查看。流大小字段指定流的大小,它将根据远程模板 URI 的长度修改为缩小/增加。
下一个扇区指定 MiniFAT 链,它提供有关 CFB 内链式流的信息。它对 blog 来说不是一个非常重要的部分,但值得看看它的外在表现形式,链显示流是如何链接的。链中的每个单元代表一个流的 40 个字节,它一直持续到值 0xFFFFFFFE,所以第一个流为 5 个 40 字节的块 (或十六进制的 0x140)。
这可以通过读取最后一个扇区的前 320 个字节来确认。
但是,目录条目指定流大小将仅使用 320 个字节中的 275 个字节或 (0x130)
OLE 流和名字对象
像往常一样,我在下面创建了一个 OLE 流 结构图。从 OLE 流中的偏移量 0x810 开始,我们到达第一个 名字对象流。名字对象是 Microsoft 组件对象模型 (COM) 中的对象 (或组件),它引用另一个对象的特定实例。名字对象流总是以描述它是什么类型的名字对象的 CLSID 开头,然后是数据流。有相当多的别名规范,请在 此处阅读。
此 OLE 流 "指示" COM 接口加载和启动恶意超链接。此外,对于 OLE 流,LinkInfo 流还包含有关超链接的数据,并且需要对其进行修改。
我的假设是,如果能以某种方式控制 OLE 流、LinkInfo 流及其组件的大小,就可以生成不同的超链接。
对我和读者来说幸运的是,这个博客是我成功后的结果,首先让我们命名重要的大小字段:
[*]NativeDataSize – 该字段指定整个 OLE Data 对象的值的大小,除非我修改和重建 MiniFAT 链和 FAT 链,否则该值不能轻易修改
[*]Directory Stream Size - 此字段指定每个流的大小,重要的是要注意大多数流不使用它们的全部大小限制并且用空字节填充
[*]OLE Stream AbsoluteMonikerStreamSize – Th is 字段指定整个 HyperLinkMoniker 及其组件的大小
[*]URLMoniker length - 此字段指定 URI 字符串的字节大小加上 24 (真的,我知道这很奇怪 - 但它在 URLMoniker 规范中指定)
创建 OLE 流
需要解决的第一个问题是流的总大小,而我可以自己手动调整 CFB 文件,一个更简单的解决方案是生成一个非常大的 OLE 流。我只是输入了一个不同大小的端口,其值为 65535,而不是使用博客开头提到的 Follina 的 Cas Van Cootens POC 代码而不是默认的 80,这反过来会生成一个非常大的流。这一次 NativeDataSize 包含值 0xC00,比之前大 512 字节 (仅大一个扇区)。
此外,MiniFAT 链现在要大得多。
因此,这标志着问题已解决。
接下来,我决定查看 RootEntry 流大小字段,目前它设置为 0x142 (322) 字节。
但我知道它填充了空字节以符合 64 字节的 MiniFat 扇区大小规范。
所以本质上,这个流大小可以增加到 0x180 (384) 字节,LinkInfo 对象也可以实现相同的功能。
当前的 LinkInfo 流大小为 0xf0 (240),但可以增加到 0x240 (576) 字节!这就像更改 blob 中的值一样简单。
最后两个问题很容易解决,通过一些简单的数学计算来计算对象的总大小,从原始大小中减去修改后的 URL 字段,并在适当的位置用零填充剩下的内容。
通过这样做,我基本上只是在修改对象而不是更改流对齐方式!这实际上非常有效!这是 Follina 的演示,其中 VLAN 中的一个 VM 以通常较长的 URI 为负载提供服务,而 VLAN 中的另一个 VM 使用 RTF 脚本检索负载。
Python 代码
我希望您喜欢阅读本文并了解有关 OLE 对象和 Microsoft Magic 的知识!下次见!
页:
[1]