PDF阅读器字体解析引擎的测试方法
赵刚 1 , 于悦 2 , 黄敏桓 1 , 王玉迎 3 , 王嘉捷 3 , 孙晓霞 1     
1. 信息系统安全技术国家重点实验室, 北京 100101;
2. 北京邮电大学 计算机学院, 北京 100876;
3. 中国信息安全测评中心, 北京 100085
摘要:PDF文档具有良好的移植性且应用广泛,常被用作恶意代码的载体。PDF文档具有严格的格式校验,对结构复杂的PDF阅读器进行模糊测试时,传统随机模糊测试效率较低。现有基于文件格式的灰盒模糊测试,由于模型描述语言能力不足,难以针对某种文件格式构建统一的数据模型。该文针对PDF阅读器字体解析引擎提出一种批量化构造测试用例的方法。通过对字体文件重构和添加辅助信息方式,构造格式统一的测试用例,对TrueType格式文件构造统一数据模型。在此基础上,开发了模糊测试工具并对20余款PDF阅读器进行了测试,触发了大量崩溃。结果表明:该方法可以有针对性地构造测试用例,并有效地挖掘PDF阅读器中的缺陷。
关键词PDF阅读器    模糊测试    测试用例构造    TrueType字体    
Test method for the font parser in PDF viewers
ZHAO Gang1, YU Yue2, HUANG Minhuan1, WANG Yuying3, WANG Jiajie3, SUN Xiaoxia1     
1. National Key Laboratory of Science and Technology on Information System Security, Beijing 100101, China;
2. School of Computer Science, Beijing University of Posts and Telecommunications, Beijing 100876, China;
3. China Information Technology Security Evaluation Center, Bejing 100085, China
Abstract: PDF files are portable and widely used, so they often host malware. Traditional PDF viewers fuzzing algorithms cannot work well due to their strict format validation. Also, existing file-format based grey-box fuzzing cannot be easily used to build a uniform data model because of the limits of its descrition language. This paper presents a method for generating test cases to test the font parser of PDF viewers. The system reconstructs the font files and adds supportive information to build a uniform data model for TrueType files. A fuzzer is built into the method and evaluated on more than twenty PDF viewers to identify several vulnerabilies. Tests show that this method can effectively generate test cases and detect bugs in PDF viewers.
Key words: PDF viewers     fuzzing     test cases generation     TrueType font    

据美国国家漏洞数据库(NVD)[1]统计,截止到2016年有2 449个PDF相关的漏洞,其中有30个与PDF文件中字体模块相关。如CVE-2010-2883、Adobe Reader和Acrobat 9.4之前版本CoolType.dll中存在缓冲区溢出漏洞,攻击者可以根据TrueType字体中SING描述表解析缺陷,通过嵌有特殊字体的PDF文件来触发漏洞。

近年来,针对文件格式漏洞,比较有效的发掘方式是模糊测试(fuzzing)技术。它借助结构多样的样本进行变异生成大量测试用例。例如, 王铁磊等[2-3]综合利用动态污点分析、符号执行等技术生成测试用例,检测程序的整数溢出漏洞。针对复杂大型软件,Godefroid等[4-6]对白盒测试深入研究并设计了SAGE;文[7-8]通过研究污点分析技术和并行化测试技术来使得模糊测试更高效。文[9]表明:单纯基于符号执行的测试用例生成技术在错误发现方面,实用性不如基于随机变异的模糊测试技术。另外,对输入文件结构较简单的处理程序,文[10-15]通过随机变异输入文件和动态符号执行来辅助生成测试用例。但是对于PDF阅读器,大多数随机生成的文件为无效文件,无法通过严格的文件格式校验。

对于模糊测试工具,大量的、高质量的合法输入数据的测试用例决定了测试的成败。测试PDF阅读器字体解析引擎的一个严峻挑战是如何找到一个合适的测试用例集。

为此,本文提出一种针对PDF阅读器字体解析引擎构造测试用例的方法。通过制作无压缩PDF模板,调整内部结构嵌入字体对象,提高了模糊测试效率。此外,根据TrueType字体文件格式定义了统一格式描述规则和模板,借助输入文件的结构信息进行种子筛选,比为所有种子收集执行信息大幅减少了开销。

1 字体与PDF文件结构 1.1 TrueType字形数据文件结构

TrueType字形渲染过程中主要包括2部分操作:字形轮廓的描述与对字形轮廓进行修正。TrueType内部主要包括一系列用于描述字体轮廓的坐标点和对轮廓进行修正的指令信息。TrueType字体文件由一系列连接的描述表组成。每个描述表必须长对齐,在必要时用零填充。除了必须首先出现在字体文件中的字体目录之外,组成字体的描述表可以按任何顺序显示。

字体文件物理结构以字体类型、描述表数量和入口地址等信息作为文件头开始(如表 1),共12 b。第一个描述表是字体描述目录,描述字体内部表的索引,便于访问后面出现的包含字体数据的一系列描述表;随后是一系列可以按任何顺序显示的描述表,一些表是所有字体所必需的,一些是可选的,这取决于特定字体期望的功能;最后是实际的描述表,位置由offset和length决定。根据Apple TrueTypeFont官方文档显示有47个表,常用的有24个,其中15个是可选的表,9个是必备的。

表 1 TrueType字体文件头信息
类型 表项 说明
uint32 sfnt_ version 文件版本号码(0X00010000)
uint16 numTables 描述表数目
uint16 searchRange 描述表快速查找范围
uint16 entrySelector 描述表入口
uint16 rangeShift 范围调整

字体文件中的每个表必须有自己的表目录条目,内容如表 2所示。

表 2 描述表目录
类型 表项 说明
unit32 tag 4 b标识符
unit32 checksum 描述表内容的效验和
unit32 offset 描述表的位置偏移
unit32 length 描述表字节长度

1.2 PDF文件格式

1) PDF文件物理结构。

PDF文件的物理结构大体分为以下几部分:文件头(header),位于文件首行,主要用来标明PDF文件属于哪一版本;文件体(obj),作为PDF文件的重要组成部分,由一系列对象构成;交叉引用表(xref),列举了文件体部分各对象的偏移信息,作为各对象的索引方便主体对象的存取;Trailer作为交叉引用表的摘要,对作者和对象等信息进行概括;文件尾,记录交叉引用表的偏移,方便PDF阅读器迅速定位交叉引用表和具体对象。

2) PDF文件逻辑结构。

常用的文档中只是单纯文字或图片内容的很少,还可能会包含目录、视频和声音等。在PDF文档内部不同级别的元素存在具体逻辑关系。PDF阅读器在解析PDF文档时按照逻辑关系,由根对象开始,寻找页面组、页面、具体内容的逻辑顺序解析。PDF文件内部对象之间的逻辑关系对应物理结构表现为各对象相互的间接引用。

PDF复合文档紧密的逻辑结构与物理结构关系,内含丰富的格式校验属性,另外PDF阅读器强大的纠错能力,使得运用传统的模糊测试技术测试PDF解析软件效率较低,大多数变异生成的测试用例会由于无法通过文档完整性校验而成为无效测试用例。因此,对PDF阅读器进行模糊测试必须考虑PDF文件内部结构信息。

PDF文件多级逻辑结构组织形式如图 1所示。

图 1 PDF逻辑结构

结合前面对PDF文件物理结构和逻辑结构的分析,图 1中根节点对应主体中的Root对象,由根对象可以间接访问到操作和页面组等内容。在PDF文件解析时,根据文件尾得到第一个交叉引用表位置,然后获得根对象,定位到根节点开始逻辑结构层层深入解析,直至解析完所需内容。

2 文件重构与模板结合

虽然字体的官方网站给出了标准文件格式,经过分析发现,从网络上收集到的字体文件结构多样化。主要表现在以下几点:字体内部包含的描述表数量和顺序不同、相同的描述表存在不同版本格式甚至有描述表内部需要4 b对齐。现有模板描述语言无法直接构造高效的统一模板,如果针对每一个测试用例单独构造模板将会花费大量人力。

在数据挖掘过程中新数据类型对找合适的分类器会带来巨大挑战,Yin等[12]提出对数据进行预处理的方法。本文通过对字体文件进行重构,调整字体内部各表的顺序并添加辅助信息辅助测试工具,根据数据模型识别字体文件各描述表内容,从而弥补模板语法描述能力不足问题。根据TrueType字体官方文档,对字体数据格式和数据间的各种关系构建字体模糊测试模型。数据格式有字符串、整数、比特位和字节流,关系包括数据块的大小、偏移和校验和等。

下面举例说明字体文件重构与模板描述相结合使测试时识别字体内部cmap各子表的内容并与特定数据模型相对应,根据cmap内部结构及解析流程,表数据需要4 b对齐。由于表数据的不确定因素导致了模板描述困难, 模板无法获得相关属性的具体长度和下一个变量的开始位置,所以添加辅助信息来辅助模型获取当前属性的长度及下一个属性的具体开始偏移位置。如图 2所示,算法1为cmap描述表重构中的部分操作,首先对旧cmap描述表文件头进行解析获得概括信息Summary,包括cmap描述表的整体长度和各子表的字典信息,字典记录子表的偏移和长度。然后从Summary中提取offsetList偏移列表并排序,获取第一个子表的偏移。可能需要向文件中添加辅助信息,检测整个概括信息长度是否满足4 b对齐。例如,概括信息为31 b,需要补充1 b的0更新cmap描述表的概要信息构造新的cmap描述表,包括更新整个cmap描述表的长度和各子表的新偏移。在每个子表前添加辅助token信息“formatxxx”,最终由辅助信息和原始信息构成新的cmap描述表,如图 3所示。对应的模板如下:模糊测试工具在解析种子用例时遇到“formatxxx”便会识别出是一个新的子表。

图 2 算法1

图 3 新的cmap描述表

解析时如果遇到“formatxxx”,将会以cmap_format_4_block描述格式进行解析,根据“formatxxx”可以识别出此描述块的结束位置与下一个描述块的开始位置。由此可以动态识别出此模块具体长度,自动判断内部某个元素出现次数。例如glyphIndexArray会根据本描述块的长度,自动判断长度为8位无符号大端的数据出现次数(介于0~256次之间)。

3 种子筛选

由于从网络上收集来的字体文件结构不同,类型不一,正确性很难保证(如有些字体校验和部分不正确)。因此本文先对原始样本进行了过滤,根据文件Hash值去除重复样本,对仓库中的字体文件进行解析,提取校验和、偏移和长度等属性进行正确性验证,移除错误字体,修正错误校验和,得到符合标准格式的字体库。只有保证字体结构格式正确前提下,后续模糊测试才能根据本文构造的模板智能高效解析和变异测试用例。

测试用例集数量庞大,存在较多的冗余,即这些测试用例的某些子集也能满足测试需求。由于运用搜集的所有的测试用例进行模糊测试将耗费大量的时间、人力和物力,测试成本太高,因此需要对测试用例进行筛选。Rebert等[13]使用代码块覆盖情况来作为筛选依据,需要耗费大量CPU时间来对数量庞大的样本文件收集代码覆盖情况。对于不平衡类数据集,Yin等[14]提出了一种利用数据特点进行分类的方法。

对字体结构的详细分析可知,字体文件描述表具有相对独立,字体内部各描述表组织结构清晰的特点,根据字体文件结构信息筛选覆盖不同描述表的字体集合,可以测试到字体解析引擎不同代码块的功能。

本文通过对字体描述目录内容解析,统计每个字体所包含的描述表信息(统计信息见表 3),根据字体样本的统计信息,筛选部分PDF文件作为种子用例。

表 3 字体描述表统计信息
文件 标签 名称 LTSH PCLT VDMX
dol-1.ttf 1 1 0 0 0
fd-40.ttf 1 1 1 0 1
fwm-70.ttf 1 1 0 1 1
gf-127.ttf 1 1 1 1 0

说明:第一行是一个较全的描述表名集合;第一列表示字体的名字,由来源的缩写与数字编号组成,方便后续分析;0代表对应的描述表不包含在此字体内,1代表此描述表包含在此字体内。

字体样本筛选流程中,首先通过预处理操作收集字体内描述表覆盖信息,考虑到测试效率问题,会在预处理阶段去除较大的测试用例,在确保能达到测试目的的前提下,尽量将测试用例设计得小一些,在测试时,解析所消耗CPU时间较短。本文将超过3 Mb文件剔除,运用剩下的作为测试用例。种子筛选算法如图 4所示。

图 4 算法2

函数SelectFonts中F为参数,是对收集来字体进行过滤后剩下的字体样本集;A字典表示的是所有可能出现的描述表存在情况,记录了已被字体集C覆盖的描述表,初始时所有键对应的值均为0;‘head’为具体描述表标签;C集合表示被选中的字体集合,初始时为空;S是随机选择的字体。函数的作用是筛选一个覆盖尽可能多的可能出现的描述表的集合。为避免所选集合来源相同,上述算法随机选择下一个检测的字体对象。TrueType字体由一系列描述表组成,遍历S中的每个描述表,如果覆盖了原本收录的字体集所未覆盖的描述表,将会被加入所选字体集合中,更新字典A。算法2为一轮的筛选,可以进行多轮筛选,然后组成一个冗余度较小的集合。

4 模块化构造测试用例

构建测试用例是测试中的重要手段。在开展软件测试工作时,研究与测试用例相关的问题是至关重要的。PDF复合文档内嵌了字体、文字、图形、电子表格、图像、声音、视频以及其他信息,数据结构多样,对象层次复杂,本文在PDF文件格式透彻研究的基础上,正确解析PDF文档的内部格式,将字体对象内嵌到PDF文档中形成有效的测试用例。由于PDF文件内部逻辑结构存在依赖关系,直接进行替换会导致字体无法被解析甚至无法通过文件格式的校验。

根据PDF文件结构及解析过程,对解压后的模板PDF文件进行内部结构调整并模块化,运用重构后的字体快速高效地产生测试用例。具体实现如图 5所示。

图 5 字体嵌入方法

首先对嵌有字体文件的PDF模板进行解压(经过实验,最终选择用Word生成嵌有字体的PDF文件,用Origami进行解压),调整PDF样本F0内部对象顺序,将字体对象调至对象列表最后部分,随后修改交叉引用表内相关偏移属性,保证各对象能够被正确解析。将调整后的PDF文档F0′分为3个模块:字体前文件F1(文件头和对象集合)、字体后文件F2(交叉引用表和文件尾)和字体。后续从重构后的字库中选择新字体直接替换字体模块,与文件F1和文件F2拼接,修正PDF中与字体相关的属性(如长度)即可得到嵌入字体的PDF文件,达到快速构造批量有效测试用例的效果。

5 实验评估 5.1 基于数据模型的模糊测试评估

为说明本文针对PDF阅读器字体解析引擎测试用例构造方法的有效性,设计了如下实验。

首先,运用相同的测试用例集,相同环境下针对基于粗糙模板和运用细化模板与字体重构相结合的方式进行实验对比,实验结果如表 4所示。

表 4 基于不同模板产生不同崩溃数量
模板类型 EXP PEX PNE UNK
粗略模板 4 4 1 9
详细模板 28 45 4 81
注:EXP=可利用的, PEX=可能可利用的, PNE=可能不可利用的, UNK=未知。

根据表 4,运用本文提出的测试用例构造方法结合针对TrueType类型字体构造的统一数据模型进行模糊测试效率高于基于粗糙模板的模糊测试。

运用相同的测试用例,针对相同测试目标运用基础模糊测试框架(basic fuzzing framework, BFF)和基于细化模板进行对比实验,崩溃数量如表 5所示。

表 5 各种崩溃的总数量
模板类型 EXP PEX PNE UNK
BFF 590 181 2 289
PeachFuzzer+粗略模板 130 5 5 28
PFF+详细模板 2 620 253 28 2 761

BFF为运用随机变异策略的模糊测试,针对具有复杂严格格式校验的PDF文档,变异后生成新的测试用例有效性较低。PeachFuzzer与简单粗糙的模板配合应用,由于模板较粗糙,对内部文件结构描述简单,变异策略简单,变异到缺陷部分概率较小,且新测试用例有效性低。本文PFF(PDF font fuzzer)对原始样本预处理,统一文件描述方式,构建统一数据模型,详细地描述了文件内部结构,有助于后续针对每个属性的变异,变异粒度更细。表 5中数据为没有去重的崩溃总数量,可以发现,基于PeachFuzzer修改后另加字体预处理等功能的PFF效率高于Cert的BFF,高于PeachFuzzer基于粗糙模板的效率。

5.2 种子筛选算法对比

为了说明本文提出的借助文件内部结构筛选种子策略的有效性,设计以下对比实验。

1) 筛选策略1:基于文件描述表覆盖情况统计信息,经过3轮筛选获得61个样本。(即本文节4提出的筛选方法)

2) 筛选策略2:在包含5 314个样本的样本库中随机选取61个样本。

3) 筛选策略3:根据前期粗略收集的样本覆盖情况,按照各样本完成的代码块数排序,然后以均等数量为间隔取61个样本。

对比3种不同筛选策略,实验环境均相同,操作系统为XP 64位,所测软件均为阅读器专家。将61个种子放在两台虚拟机同时测试,运行相同时间(72 h)后,收集并观察各策略对应的崩溃信息(见表 6)。

表 6 不同种子筛选策略对应产生不同崩溃数量和崩溃总数量
筛选策略 EXP PEX PNE UNK 崩溃总数
1 20 20 3 64 4 243
2 17 26 6 62 1 553
3 19 21 4 51 1 668
注:上表中不同崩溃数量代表由msec产生的不同标签数量,崩溃总数代表产生的崩溃次数。两者属于一对多关系,一个标签可能对应多个崩溃。

表 6数据可以发现,运用筛选策略1得到的种子集对应的不同崩溃数量中可利用的数量多于运用其他2种策略筛选所得种子对应的不同崩溃数量。从崩溃的总数量角度也说明基于文件内部结构筛选策略的有效性。

6 总结

本文针对PDF字体解析引擎构造格式统一的测试用例,与TrueType数据模型相结合对PDF阅读器进行灰盒测试。借助字体文件内部结构信息进行样本筛选,以低开销方式获取了覆盖面较广的测试用例集。运用该方法构造的测试用例与数据模型对20余款PDF阅读器进行了测试,触发了大量崩溃。发现Xchange View存在整数溢出漏洞,阅读器专家中存在可执行任意代码的高危漏洞,多款软件存在本地拒绝服务现象。下一步工作包括研究崩溃探测的原理,改进崩溃监控的方法,对测试结果进一步筛选提取,减少相同原因导致的崩溃数量。

参考文献
[1] US-CERT Security Operations Center. National vulnerability database. . https://nvd.nist.gov/.
[2] WANG T L, WEI T, LIN Z Q, et al. IntScope: Automatically detecting integer overflow vulnerability in X86 binary using symbolic execution[C]//Proceedings of the 16th Network and Distributed System Security Symposium. San Diego, USA: Internet Society, 2009: 1-14.
[3] WANG T L, WEI T, GU G F, et al. TaintScope: A checksum-aware directed fuzzing tool for automatic software vulnerability detection[C]//Proceedings of 2010 IEEE Symposium on Security and Privacy. Berkeley/Oakland, USA: IEEE, 2010: 497-512.
[4] GODEFROID P, LEVIN M Y, MOLNAR D A. Automated whitebox fuzz testing[C]//Proceedings of the 15th Annual Network and Distributed System Security Symposium. San Diego, USA: Internet Society, 2008: 1-16.
[5] GODEFROID P, KIEZUN A, LEVIN M Y. Grammar-based whitebox fuzzing[C]//Proceedings of the 29th ACM SIGPLAN Conference on Programming Language Design and Implementation. Tucson, USA: ACM, 2008: 206-215.
[6] GODEFROID P, LEVIN M Y, MOLNAR D A. SAGE:Whitebox fuzzing for security testing[J]. Communications of the ACM, 2012, 55(3): 40–44. DOI:10.1145/2093548
[7] WANG X F, MA H T, JING L S. A dynamic marking method for implicit information flow in dynamic taint analysis[C]//Proceedings of the 8th International Conference on Security of Information and Networks. Sochi, Russia: ACM, 2015: 275-282.
[8] ISAEV I K, SIDOROV D V. The use of dynamic analysis for generation of input data that demonstrates critical bugs and vulnerabilities in programs[J]. Programming and Computer Software, 2010, 36(4): 225–236. DOI:10.1134/S0361768810040055
[9] STEPHENS N, GROSEN J, SALLS C, et al. Driller: Augmenting fuzzing through selective symbolic execution[C]//Proceedings of the Network and Distributed System Security Symposium. San Diego, USA: Internet Society, 2016: 21-24.
[10] HOUSEHOLDER A D, FOOTE J M. Probability-based parameter selection for black-box fuzz testing[R]. Pittsburgh: CMU, 2012.
[11] CHEN T, ZHANG X S, GUO S Z, et al. State of the art:Dynamic symbolic execution for automated test generation[J]. Future Generation Computer Systems, 2013, 29(7): 1758–1773. DOI:10.1016/j.future.2012.02.006
[12] YIN H, GAI K K. An empirical study on preprocessing high-dimensional class-imbalanced data for classification[C]//Proceedings of the 17th International Conference on High Performance Computing and Communications. New York, USA: IEEE, 2015: 1314-1319.
[13] REBERT A, CHA S K, AVGERINOS T, et al. Optimizing seed selection for fuzzing[C]//Proceedings of the 23rd USENIX Conference on Security Symposium. San Diego, USA: USENIX Association Berkeley, 2014: 861-875.
[14] YIN H, GAI K K, WANG Z J. A classification algorithm based on ensemble feature selections for imbalanced-class dataset[C]//Proceedings of the 2nd International Conference on Big Data Security on Cloud (BigDataSecurity), IEEE International Conference on High Performance and Smart Computing (HPSC), and IEEE International Conference on Intelligent Data and Security (IDS). New York, USA: IEEE, 2016: 245-249.
[15] KARGEÉN U, SHAHMEHRI N. Turning programs against each other: High coverage fuzz-testing using binary-code mutation and dynamic slicing[C]//Proceedings of the 10th Joint Meeting on Foundations of Software Engineering. Bergamo, Italy: ACM, 2015: 782-792.