Fuzzing101-Exercise1 fuzz xpdf CVE-2019-13288
author: cxing
date: 2023年4月28日
0x00 前期准备
第一个exercise是复现xpdf的 CVE-2019-13288,在正式进入fuzz之前我们需要了解xpdf和 CVE-2019-13288。
找到xpdf的官网,上面有一句简短的介绍。
Xpdf is a free PDF viewer and toolkit, including a text extractor, image converter, HTML converter, and more. Most of the tools are available as open source.
Xpdf是一个免费的PDF查看器和工具包,包括一个文本提取器, 图像转换器、HTML 转换器等。大多数工具都是作为开源提供。
简言之,xpdf项目提供了PDF阅读应用,以及一些其他文档类型与pdf转换的工具包,具体的编译出来提供的几个工具如下图:
我们收集一下关于CVE-2019-13288的信息,阿里云漏洞库上描述如下:
Xpdf是Foo实验室的一款开源的PDF阅读器。该产品支持解码LZW压缩格式的文件以及阅读加密的PDF文件。
Xpdf 4.01.01版本中的Parser.cc文件的‘Parser::getObj()’函数存在安全漏洞。攻击者可借助特制的文件利用该漏洞造成拒绝服务(无限递归)。
0x01 环境搭建
如果你缺少gcc编译相关的组件,请执行下面命令
sudo apt install build-essential
在你的工作目录下载必要的资源,你也可以参考我工作目录的设置。
cxing@cxing-virtual-machine:~/fuzzing/xpdf$ wget https://dl.xpdfreader.com/old/xpdf-3.02.tar.gz
cxing@cxing-virtual-machine:~/fuzzing/xpdf$ tar -xvzf xpdf-3.02.tar.gz
我们先构建一个用于调试与后续分析crash、hangs例子的版本
cxing@cxing-virtual-machine:~/fuzzing/xpdf/xpdf-3.02$ CFLAGS="-g O0" ./configure --prefix=/home/cxing/fuzzing/xpdf/dbg_install
cxing@cxing-virtual-machine:~/fuzzing/xpdf/xpdf-3.02$ make -j4 && make install
下载我们fuzz所需要的语料库。
cxing@cxing-virtual-machine:~/fuzzing/xpdf$ mkdir pdf_examples && cd pdf_examples
cxing@cxing-virtual-machine:~/fuzzing/xpdf/pdf_sample$ wget https://github.com/mozilla/pdf.js-sample-files/raw/master/helloworld.pdf
cxing@cxing-virtual-machine:~/fuzzing/xpdf/pdf_sample$ wget http://www.africau.edu/images/default/sample.pdf
cxing@cxing-virtual-machine:~/fuzzing/xpdf/pdf_sample$ wget https://www.melbpc.org.au/wp-content/uploads/2017/10/small-example-pdf-file.pdf
0x02 Fuzzing
我使用的是官方提供的AFL++ docker镜像进行fuzz。官方docker镜像提供了最新版本的AFL++,你可查阅AFL++的官方GitHub页面。具体的你可以使用下面命令安装。
docker pull aflplusplus/aflplusplus
启动我们的docker镜像,并且设置一个与主机共享目录。其中-v参数后的第一个/home/cxing/fuzzing
是宿主机的目录,第二个则是docker镜像映射的目录,如果没有这个目录docker会自动创建出这样一个目录,我建议映射的目录与主机目录一致,后续进行堆栈回溯分析crash时会更加方便。
注1:具体的,docker镜像中的编译会根据绝对路径填充一些调试信息,而我们调试主要在主机中调试,那么调试信息保持与主机路径的一致性会方便我们调试分析crash。
sudo docker run -it -v /home/cxing/fuzzing:/home/cxing/fuzzing aflplusplus/aflplusplus
下面我们将用AFL提供的编译工具,构建一个fuzz版本,具体命令如下。
注2:其中CC和CXX是显示的指定C和C++编译器,configure文件会根据我们的指定替换到默认的编译器。
prefix参数是显示指定编译后安装软件的路径,我们只是fuzz xpdf并不是真的希望安装,因此我们指定该参数。
[afl++ d8ea5cca7628] /home/cxing/fuzzing/xpdf/xpdf-3.02 # CC=afl-clang-fast CXX=afl-clang-fast++ ./configure --prefix=/home/cxing/fuzzing/xpdf/fuzz_install
[afl++ d8ea5cca7628] /home/cxing/fuzzing/xpdf/xpdf-3.02 # make -j4
[afl++ d8ea5cca7628] /home/cxing/fuzzing/xpdf/xpdf-3.02 # make install
[afl++ d8ea5cca7628] /home/cxing/fuzzing/xpdf # ls
dbg_install fuzz_install pdf_sample xpdf-3.02 xpdf-3.02.tar.gz
在进行fuzz之前,我个人的习惯是在本次bash shell中构建一些零时的环境变量,以方便我们执行fuzz相关的命令和后crash分析,具体如下:
[afl++ d8ea5cca7628] /home/cxing/fuzzing/xpdf/fuzz_install/bin # pwd
/home/cxing/fuzzing/xpdf/fuzz_install/bin
[afl++ d8ea5cca7628] /home/cxing/fuzzing/xpdf/fuzz_install/bin # export fuzz_bin=/home/cxing/fuzzing/xpdf/fuzz_install/bin
[afl++ d8ea5cca7628] /home/cxing/fuzzing/xpdf/pdf_sample # pwd
/home/cxing/fuzzing/xpdf/pdf_sample
[afl++ d8ea5cca7628] /home/cxing/fuzzing/xpdf/pdf_sample # export fuzz_in=/home/cxing/fuzzing/xpdf/pdf_sample
那么我们正式开始fuzz。我们对pdftotext和pdfinfo两者都进行fuzz,因此你需要运行两个docker,这部分我就不多谈。
[afl++ f462d699ca02] /home/cxing/fuzzing/xpdf # afl-fuzz -i $fuzz_in -o fuzz_out -- $fuzz_bin/pdftotext @@ ./output
[afl++ f462d699ca02] /home/cxing/fuzzing/xpdf # afl-fuzz -i $fuzz_in -o afl_out -- $fuzz_bin/pdfinfo -box @@
pdftotext中运行了两分钟发现了一个crash和一个hangs。
pdfinfo运行了一分钟不到发现了32个crash,并且还在快速增加。我们
注3:关于afl 状态屏幕可以参考官网的介绍,
0x03 输入样本分析
我们先进入crash目录,具体看下这些crash。
cxing@cxing-virtual-machine:~/fuzzing/xpdf/dbg_install/bin$ pwd
/home/cxing/fuzzing/xpdf/dbg_install/bin
cxing@cxing-virtual-machine:~/fuzzing/xpdf/dbg_install/bin$ export dbg_bin=/home/cxing/fuzzing/xpdf/dbg_install/bin
cxing@cxing-virtual-machine:~/fuzzing/xpdf/fuzz_out/default/crashes$ gdb $dbg_bin/pdftotext
pwndbg> directory /home/cxing/fuzzing/xpdf/xpdf-3.02
Source directories searched: /home/cxing/fuzzing/xpdf/xpdf-3.02:$cdir:$cwd
pwndbg> set args id:000000,sig:11,src:000041,time:75103,execs:56741,op:havoc,rep:16 ./output
pwndbg> r
Starting program: /home/cxing/fuzzing/xpdf/dbg_install/bin/pdftotext id:000000,sig:11,src:000041,time:75103,execs:56741,op:havoc,rep:16 ./output
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Error (196): Illegal character '>'
Error (199): Illegal character <78> in hex string
Error (200): Illegal character <6d> in hex string
Error (201): Illegal character <70> in hex string
Error (202): Illegal character <3a> in hex string
Error (204): Illegal character <72> in hex string
Error (207): Illegal character <74> in hex string
Error (211): Illegal character <74> in hex string
Error: PDF file is damaged - attempting to reconstruct xref table...
Error (738): Dictionary key must be a name object
Error (744): Dictionary key must be a name object
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff78a3e6e in _int_malloc (av=av@entry=0x7ffff7a19c80 <main_arena>, bytes=bytes@entry=7) at ./malloc/malloc.c:3903
3903 ./malloc/malloc.c: No such file or directory.
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
───────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]────────────────────────────────────────
*RAX 0x20
*RBX 0x7ffff7a19c80 (main_arena) ◂— 0x0
*RCX 0x10
*RDX 0x7ffff7a19d00 (main_arena+128) —▸ 0x7ffff7a19cf0 (main_arena+112) —▸ 0x7ffff7a19ce0 (main_arena+96) —▸ 0x555556cec500 ◂— 0x0
*RDI 0x20
*RSI 0x7ffff7a19cf0 (main_arena+112) —▸ 0x7ffff7a19ce0 (main_arena+96) —▸ 0x555556cec500 ◂— 0x0
*R8 0x555556cec480 ◂— 0x555550052 /* 'R' */
*R9 0x7
*R10 0x7fffff7ff290 ◂— 0xa /* '\n' */
*R11 0xe745ece1bc3c63ec
R12 0x0
*R13 0x20
*R14 0x2
*R15 0x20
*RBP 0x7
*RSP 0x7fffff7fefc0
*RIP 0x7ffff78a3e6e (_int_malloc+1086) ◂— mov dword ptr [rsp + 0x24], r14d
────────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]─────────────────────────────────────────────────
► 0x7ffff78a3e6e <_int_malloc+1086> mov dword ptr [rsp + 0x24], r14d
0x7ffff78a3e73 <_int_malloc+1091> shr rax, 6
0x7ffff78a3e77 <_int_malloc+1095> shr rdi, 9
0x7ffff78a3e7b <_int_malloc+1099> mov dword ptr [rsp + 0x80], 0x6e
0x7ffff78a3e86 <_int_malloc+1110> mov qword ptr [rsp + 0x50], rax
0x7ffff78a3e8b <_int_malloc+1115> add eax, 0x30
0x7ffff78a3e8e <_int_malloc+1118> mov qword ptr [rsp + 0x60], rdi
0x7ffff78a3e93 <_int_malloc+1123> add edi, 0x5b
0x7ffff78a3e96 <_int_malloc+1126> mov dword ptr [rsp + 0x84], edi
0x7ffff78a3e9d <_int_malloc+1133> mov dword ptr [rsp + 0x5c], eax
0x7ffff78a3ea1 <_int_malloc+1137> mov dword ptr [rsp + 0x7c], 0x77
──────────────────────────────────────────────────────────────[ STACK ]──────────────────────────────────────────────────────────────
<Could not read memory at 0x7fffff7fefc0>
────────────────────────────────────────────────────────────[ BACKTRACE ]────────────────────────────────────────────────────────────
► f 0 0x7ffff78a3e6e _int_malloc+1086
f 1 0x7ffff78a52e2 malloc+450
f 2 0x5555555fcab4 copyString+36
f 3 0x5555555fcab4 copyString+36
f 4 0x5555555da276 Lexer::getObj(Object*)+1606
f 5 0x5555555da276 Lexer::getObj(Object*)+1606
f 6 0x5555555e00ce
f 7 0x5555555e0880 Parser::getObj(Object*, unsigned char*, CryptAlgorithm, int, int, int)+1296
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> bt 30
#0 0x00007ffff78a3e6e in _int_malloc (av=av@entry=0x7ffff7a19c80 <main_arena>, bytes=bytes@entry=7) at ./malloc/malloc.c:3903
#1 0x00007ffff78a52e2 in __GI___libc_malloc (bytes=7) at ./malloc/malloc.c:3321
#2 0x00005555555fcab4 in gmalloc (size=<optimized out>) at gmem.cc:97
#3 copyString (s=0x555556cec2e4 "Filter") at gmem.cc:261
#4 0x00005555555da276 in Object::initName (nameA=0x555556cec2e4 "Filter", this=0x555556cec458) at /home/cxing/fuzzing/xpdf/xpdf-3.02/xpdf/Object.h:93
#5 Lexer::getObj (this=0x555556cec2c0, obj=obj@entry=0x555556cec458) at Lexer.cc:344
#6 0x00005555555e00ce in Parser::shift (this=this@entry=0x555556cec430) at Parser.cc:226
#7 0x00005555555e0880 in Parser::getObj (this=0x555556cec430, obj=0x7fffff7ff1f0, fileKey=0x0, encAlgorithm=<optimized out>, keyLength=<optimized out>, objNum=7, objGen=0) at Parser.cc:111
#8 0x00005555555e079a in Parser::getObj (this=this@entry=0x555556cec430, obj=obj@entry=0x7fffff7ff320, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=objNum@entry=7, objGen=0) at Parser.cc:85
#9 0x00005555555f7b1d in XRef::fetch (this=0x555555695230, num=7, gen=0, obj=0x7fffff7ff320) at XRef.cc:823
#10 0x00005555555e014f in Object::dictLookup (obj=0x7fffff7ff320, key=0x55555560c1c6 "Length", this=0x7fffff7ff4e0) at /home/cxing/fuzzing/xpdf/xpdf-3.02/xpdf/Object.h:253
#11 Parser::makeStream (this=0x555556cebf50, dict=0x7fffff7ff4e0, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=7, objGen=0) at Parser.cc:156
#12 0x00005555555e06fc in Parser::getObj (this=this@entry=0x555556cebf50, obj=obj@entry=0x7fffff7ff4e0, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=objNum@entry=7, objGen=0) at Parser.cc:94
#13 0x00005555555f7b1d in XRef::fetch (this=0x555555695230, num=7, gen=0, obj=0x7fffff7ff4e0) at XRef.cc:823
#14 0x00005555555e014f in Object::dictLookup (obj=0x7fffff7ff4e0, key=0x55555560c1c6 "Length", this=0x7fffff7ff6a0) at /home/cxing/fuzzing/xpdf/xpdf-3.02/xpdf/Object.h:253
#15 Parser::makeStream (this=0x555556ceba70, dict=0x7fffff7ff6a0, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=7, objGen=0) at Parser.cc:156
#16 0x00005555555e06fc in Parser::getObj (this=this@entry=0x555556ceba70, obj=obj@entry=0x7fffff7ff6a0, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=objNum@entry=7, objGen=0) at Parser.cc:94
#17 0x00005555555f7b1d in XRef::fetch (this=0x555555695230, num=7, gen=0, obj=0x7fffff7ff6a0) at XRef.cc:823
#18 0x00005555555e014f in Object::dictLookup (obj=0x7fffff7ff6a0, key=0x55555560c1c6 "Length", this=0x7fffff7ff860) at /home/cxing/fuzzing/xpdf/xpdf-3.02/xpdf/Object.h:253
#19 Parser::makeStream (this=0x555556ceb590, dict=0x7fffff7ff860, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=7, objGen=0) at Parser.cc:156
#20 0x00005555555e06fc in Parser::getObj (this=this@entry=0x555556ceb590, obj=obj@entry=0x7fffff7ff860, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=objNum@entry=7, objGen=0) at Parser.cc:94
#21 0x00005555555f7b1d in XRef::fetch (this=0x555555695230, num=7, gen=0, obj=0x7fffff7ff860) at XRef.cc:823
#22 0x00005555555e014f in Object::dictLookup (obj=0x7fffff7ff860, key=0x55555560c1c6 "Length", this=0x7fffff7ffa20) at /home/cxing/fuzzing/xpdf/xpdf-3.02/xpdf/Object.h:253
#23 Parser::makeStream (this=0x555556ceb0b0, dict=0x7fffff7ffa20, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=7, objGen=0) at Parser.cc:156
#24 0x00005555555e06fc in Parser::getObj (this=this@entry=0x555556ceb0b0, obj=obj@entry=0x7fffff7ffa20, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=objNum@entry=7, objGen=0) at Parser.cc:94
#25 0x00005555555f7b1d in XRef::fetch (this=0x555555695230, num=7, gen=0, obj=0x7fffff7ffa20) at XRef.cc:823
#26 0x00005555555e014f in Object::dictLookup (obj=0x7fffff7ffa20, key=0x55555560c1c6 "Length", this=0x7fffff7ffbe0) at /home/cxing/fuzzing/xpdf/xpdf-3.02/xpdf/Object.h:253
#27 Parser::makeStream (this=0x555556ceabd0, dict=0x7fffff7ffbe0, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=7, objGen=0) at Parser.cc:156
#28 0x00005555555e06fc in Parser::getObj (this=this@entry=0x555556ceabd0, obj=obj@entry=0x7fffff7ffbe0, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0, objNum=objNum@entry=7, objGen=0) at Parser.cc:94
#29 0x00005555555f7b1d in XRef::fetch (this=0x555555695230, num=7, gen=0, obj=0x7fffff7ffbe0) at XRef.cc:823
(More stack frames follow...)
pwndbg>
gdb收到了Program received signal SIGSEGV, Segmentation fault.
,说明程序crash,并且我们查看堆栈回溯,发现似乎进入了Parser::getObj和XRef::fetch的无限递归调用,这正是 CVE-2019-13288 中提到的,而hangs主要就是递归漏洞,大概看了一下与我们收集到的crash一致。我们发现Parser::getObj与Xref::fetch互相循环递归调用,具体的最终的递归是从getObj开始的,那么显然它的实现存在问题,具体的可以查阅CVE-2019-13288提供的信息,里面有对该漏洞详细描述,并且最新版本的xpdf已经修复该漏洞。
我们在看一下pdfinfo中的crash,这个crash 是我自然好奇顺手fuzz了一下pdfinfo得到的,并不是fuzz 1-1中要求的。经过验证在最版本已经修复并且修复点与我个人的分析结果一直,应该是一个被发现的漏洞。
简单的看了下只有一类crash,就是空指针引用。
pwndbg> r
Starting program: /home/cxing/fuzzing/xpdf/dbg_install/bin/pdfinfo -box id:000047,sig:11,src:000002,time:53102,execs:20088,op:havoc,rep:8
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Error: PDF file is damaged - attempting to reconstruct xref table...
Error (596): Dictionary key must be a name object
Error (46): Dictionary key must be a name object
Error (52): Dictionary key must be a name object
Error (61): Dictionary key must be a name object
Error: Kid object (page 1) is wrong type (null)
Error: Page count in top-level pages object is incorrect
Tagged: no
Pages: 0
Encrypted: no
Program received signal SIGSEGV, Segmentation fault.
main (argc=<optimized out>, argc@entry=3, argv=argv@entry=0x7fffffffde58) at /home/cxing/fuzzing/xpdf/xpdf-3.02/xpdf/Page.h:55
55 PDFRectangle *getMediaBox() { return &mediaBox; }
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────[ REGISTERS / show-flags off / show-compact-regs off ]──────────────────────────────────────
*RAX 0x555555682d80 ◂— 0x0
RBX 0x0
*RCX 0x7ffff7914a37 (write+23) ◂— cmp rax, -0x1000 /* 'H=' */
*RDX 0x1
*RDI 0x55555560b89f ◂— 'MediaBox: '
*RSI 0x1
*R8 0x1
*R9 0x7fffffffda97 ◂— 0x2aba75bcf54b0030 /* '0' */
R10 0x0
*R11 0x246
*R12 0x55555560b6aa ◂— 0x32302e33006f6e /* 'no' */
R13 0x0
R14 0x0
*R15 0x7ffff7ffd040 (_rtld_global) —▸ 0x7ffff7ffe2e0 —▸ 0x555555554000 ◂— 0x10102464c457f
*RBP 0x555555681bb0 —▸ 0x555555668eb0 ◂— 0x41 /* 'A' */
*RSP 0x7fffffffdbc0 ◂— 0x0
*RIP 0x55555558e571 (main+2417) ◂— mov rsi, qword ptr [rbx + 0x10]
───────────────────────────────────────────────[ DISASM / x86-64 / set emulate on ]───────────────────────────────────────────────
► 0x55555558e571 <main+2417> mov rsi, qword ptr [rbx + 0x10]
0x55555558e575 <main+2421> call printBox(char*, PDFRectangle*) <printBox(char*, PDFRectangle*)>
0x55555558e57a <main+2426> mov rax, qword ptr [rbx + 0x10]
0x55555558e57e <main+2430> lea rdi, [rip + 0x7d32b]
0x55555558e585 <main+2437> lea rsi, [rax + 0x20]
0x55555558e589 <main+2441> call printBox(char*, PDFRectangle*) <printBox(char*, PDFRectangle*)>
0x55555558e58e <main+2446> mov rax, qword ptr [rbx + 0x10]
0x55555558e592 <main+2450> lea rdi, [rip + 0x7d328]
0x55555558e599 <main+2457> lea rsi, [rax + 0x48]
0x55555558e59d <main+2461> call printBox(char*, PDFRectangle*) <printBox(char*, PDFRectangle*)>
0x55555558e5a2 <main+2466> mov rax, qword ptr [rbx + 0x10]
────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────
In file: /home/cxing/fuzzing/xpdf/xpdf-3.02/xpdf/Page.h
50
51 // Destructor.
52 ~PageAttrs();
53
54 // Accessors.
► 55 PDFRectangle *getMediaBox() { return &mediaBox; }
56 PDFRectangle *getCropBox() { return &cropBox; }
57 GBool isCropped() { return haveCropBox; }
58 PDFRectangle *getBleedBox() { return &bleedBox; }
59 PDFRectangle *getTrimBox() { return &trimBox; }
60 PDFRectangle *getArtBox() { return &artBox; }
────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────
00:0000│ rsp 0x7fffffffdbc0 ◂— 0x0
... ↓ 2 skipped
03:0018│ 0x7fffffffdbd8 —▸ 0x555555668eb0 ◂— 0x41 /* 'A' */
04:0020│ 0x7fffffffdbe0 —▸ 0x5555556815a0 —▸ 0x5555556815e0 ◂— 0x7fff00000006
05:0028│ 0x7fffffffdbe8 ◂— 0x200000000
06:0030│ 0x7fffffffdbf0 ◂— 0xd /* '\r' */
07:0038│ 0x7fffffffdbf8 ◂— 0x0
──────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────
► f 0 0x55555558e571 main+2417
f 1 0x7ffff7829d90 __libc_start_call_main+128
f 2 0x7ffff7829e40 __libc_start_main+128
f 3 0x55555558e675 _start+37
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
pwndbg> bt
#0 main (argc=<optimized out>, argc@entry=3, argv=argv@entry=0x7fffffffde58) at /home/cxing/fuzzing/xpdf/xpdf-3.02/xpdf/Page.h:55
#1 0x00007ffff7829d90 in __libc_start_call_main (main=main@entry=0x55555558dc00 <main(int, char**)>, argc=argc@entry=3, argv=argv@entry=0x7fffffffde58) at ../sysdeps/nptl/libc_start_call_main.h:58
#2 0x00007ffff7829e40 in __libc_start_main_impl (main=0x55555558dc00 <main(int, char**)>, argc=3, argv=0x7fffffffde58, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffde48) at ../csu/libc-start.c:392
#3 0x000055555558e675 in _start ()
pwndbg>
这个空指针引用实际上是引用了doc变量中的pages二级指针也就是pages数组,而pages数组的元素却是0.因此我们从doc对象的创建开始追踪,一路回溯至pages的创立,大致的分析链条如下:
main ->
doc = new PDFDoc(fileName, ownerPW, userPW);
PDFDoc::PDFDoc ->
ok = setup(ownerPassword, userPassword);
PDFDoc::setup ->
xref = new XRef(str);
//skip code
catalog = new Catalog(xref);
注意因为最终pages的寻找和计算都是通过PDFDoc的catalog成员调用其方法与内部成员实现的,因此我们怀疑在new Xref时对标签的解析存在问题,以至于Catalog依据xref得到了错误的信息。
当我下载最新版本的xpdf验证该crash时,给出了如下信息:
cxing@cxing-virtual-machine:~/fuzzing/xpdf/xpdf-4.04/build/xpdf$ ./pdfinfo -box /home/cxing/fuzzing/xpdf/afl_out/default/crashes/id:000001,sig:11,src:000001,time:5606,execs:2503,op:havoc,rep:8
Syntax Error: Couldn't read xref table
Syntax Warning: PDF file is damaged - attempting to reconstruct xref table...
Syntax Error (2963): Dictionary key must be a name object
Syntax Error (2966): Dictionary key must be a name object
Syntax Error: Page tree reference is wrong type (integer)
Syntax Error: Page tree reference is wrong type (integer)
Syntax Error: Page tree reference is wrong type (cmd)
Syntax Error: Page tree reference is wrong type (integer)
Syntax Error: Page tree reference is wrong type (cmd)
Syntax Error: Invalid page count in page tree
Syntax Error: Invalid page count in page tree
Tagged: no
Form: none
Pages: 2
Encrypted: no
Page size: 50 x 50 pts (rotated 0 degrees)
MediaBox: 0.00 0.00 50.00 50.00
CropBox: 0.00 0.00 50.00 50.00
BleedBox: 0.00 0.00 50.00 50.00
TrimBox: 0.00 0.00 50.00 50.00
ArtBox: 0.00 0.00 50.00 50.00
File size: 2986 bytes
Optimized: no
PDF version: 1.3
其中提到了Syntax Error: Couldn't read xref table,这与我们猜测一致,即旧版本中对引用解析存在问题,但显然这个问题新版本已经修复。