Fuzzing101-Exercise1 fuzz xpdf CVE-2019-13288

发布时间 2023-04-28 17:54:46作者: 辰星-cxing

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转换的工具包,具体的编译出来提供的几个工具如下图:
image.png

我们收集一下关于CVE-2019-13288的信息,阿里云漏洞库上描述如下:
image.png

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。
image.png
pdfinfo运行了一分钟不到发现了32个crash,并且还在快速增加。我们
image.png

注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,这与我们猜测一致,即旧版本中对引用解析存在问题,但显然这个问题新版本已经修复。