深入 CFF 字体,并偶然从某个越狱中了解到 cff 解析中存在的一个 bof(缓冲区溢出)。纯属娱乐。
那么……CFF 到底是什么?据我所知,它是一种文件格式,但让我们试着看看维基百科怎么说:“CFF 充当一个容器,可以将多种字体存储在一个称为 FontSet 的单元中。”(https://docs.fileformat.com/font/cff/)。所以基本上我们可以把字体存储在一起。酷,那接下来呢?嗯,既然它是一种文件格式,那它就必须有某种规范。没错,它确实有,而且并不友好,因为规范有 60 页,我可不想去学它的格式。但我们可以使用 star-master 的 GitHub 仓库中的漏洞利用代码(我想是来自 jailbreak 2.0 的源代码)来了解它的格式。
那么……我们首先使用作者提供的 cff.py 脚本,同时用 hxd editor 打开 out.cff(标准的简单 .cff 文件)文件。
我们可以看到,当把文件交给解析器时,会得到以下结果

所以我们已经有了一些进展,因为我们已经能为 cff 给出一个定义了:我们可以得出结论,它的头部中有一些元数据,我猜这些元数据指示了 tff 版本、headersize 和 absoffsize(不管那可能是什么)。
所以到目前为止 |主版本号(1字节)|次版本号(1字节)|头部大小(1字节) | 头部绝对偏移大小(1字节) |
接着我们继续,会看到有一个函数用来获取这个 .cff 文件中正在使用哪些字体。如图所示

那么我们从哪里知道它的目的是读取正在使用的字体呢?嗯,运行该工具后,我们在 cmd 中得到了以下结果

我们可以看到这些是文件元数据之后的字节

我们稍后会再回来分析这个函数,但现在我们可以推断出文件格式为 |主版本号(1字节)|次版本号(1字节)|头部大小(1字节) | 头部绝对偏移大小(1字节) | ABCDEF+包中的字体|
此外,我们会在脚本中搜索被称为 string 的东西:

为什么这样做?因为我认为他们想要收集有关包中所用字体的信息。根据文档,他们是这样说的:“除出现在 Name INDEX 中的 FontName 和 CIDFontName 字符串外,FontSet 内不同字体使用的所有字符串都会被收集到一个 INDEX 结构中,并通过一个称为字符串标识符或 SID 的 2 字节无符号数来引用。这些字符串被称为标准字符串,它们描述了 ISOAdobe 和 Expert 字符集中使用的所有名称。”不管怎样,这里值得注意的一点是,我们跳过了大约 41 个字节才到达字符串。


接下来,我们获取有关 .cff 文件中存在的字体的信息

这到底是怎么做到的?好吧,我们收集了所谓的 top dict 数据。那是什么鬼?嗯,根据我能从文档中理解到的,它是一个 python dict,其中包含某些信息,并以某种方式编码。

巧合的是,如果我们遵循解码算法:

我们会对 strings 数据类型进行解引用,以获取字体信息,因此我们得出结论:topdicts 只是包含一些索引,这些索引稍后会在 strings 数据类型中使用,以获取字体信息。
所以到目前为止,文件格式的定义仍然有效,
|主版本号(1字节)|次版本号(1字节)|头部大小(1字节) | 头部绝对偏移大小(1字节) | ABCDEF+包中的字体|41 字节已知|25 字节字体信息|
酷,那接下来会发生什么?
嗯,如果我们检查解析器脚本,我们会看到它会获取 charstring_off、private_off,并会移动到 charstring_off 位置读取更多内容

但这如何帮助我们理解更大的图景呢?所以我就要突然收尾了。基本上,我对两个文件做了 diff,一个是正常的 cff,另一个是损坏的 cff。

左边是损坏的 .cff 文件,右边是正常的 .cff 文件。如果我们检查运行时结果

第一次运行解析时,我们看到 count、offsize、offbase 等一切只是到某些分隔符为止的偏移量。什么分隔符?准确地说,是字体的名称。所以正如你看到的('offbase', 8L),也就是从文件开头到第一次遇到 cff 文件中字体字符串之间的偏移。

在“第二次运行解析”中也可以看到

我们在十六进制查看器的 0x6c 偏移处看到 \x0e\0xe\0xe\x0e,这是我们恶意数据的开头。
所以总而言之,.cff 文件的一般格式为
|主版本号(1字节)|次版本号(1字节)|头部大小(1字节) | 头部绝对偏移大小(1字节) | ABCDEF+包中的字体|41 字节已知|25 字节字体信息|
以及自定义文件格式(包含用户内容)
|主版本号(1字节)|次版本号(1字节)|头部大小(1字节) | 头部绝对偏移大小(1字节) | ABCDEF+包中的字体|41 字节未知|25 字节字体信息| 4 字节(计数) | 4 字节(偏移大小) | 9 字节待记录| 用户内容|