Fuzzing101-Exercise1 fuzz xpdf CVE-2019-13288

Posted cheney辰星

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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,这与我们猜测一致,即旧版本中对引用解析存在问题,但显然这个问题新版本已经修复。

基于802.11Fuzz技术的研究

转自安全客

关于无线的Fuzz最开始接触了解时,国内基本毛线都搜不到。经过几个月的资料搜集和学习,将大约全网的fuzz资料整理翻译分析并读懂写下,就为填补国内空白,也希望无线爱好者能多多交流。

 在各个安全领域的漏洞挖掘方法中,Fuzz都挺流行的. Fuzz是一种黑盒软件测试技术,这基本上是使用畸形或半自动化的方式在一个畸形的数据注入发现执行错误,运用在协议也比较多。当源代码是不可用的时候,还是不错的,在802.11协议方面,Fuzzing的层面也比较多,特别是针对驱动和接入点的。

 

 

802.11的 State machine ,802.11标准规定的State machine

技术分享

 

这里有‘State1 ,State2,State3’这三个状态。它这写的比较模糊,不太容易懂,翻译过来有点懵逼。其实这三个State的本意该是:

State1:是用于访问点的客户端设备的初始状态。

State2:是通过对访问点进行身份验证的身份验证状态。

State3:是一个授权发送接受数据通信帧并通过无线接入点和有线网络的关联的状态转换过程,然后反馈802.11Frames.

上面都提到了,802.11标准规定状态机必须在固件或者驱动中实现,许多的驱动管理的状态为了保证它的运行,都采用例如一个AP接受了Assoc请求后,只包括一个网络配置名称,这三个状态是很重要的,Fuzz的思路就是从这三个State机制中来。

 

 

802.11还定义了三种帧类型(就是上面那Class):

Class1 Frames:允许从State1、2和3探测请求/响应,beacon,身份验证请求/响应/解除认证

Class2 Frames:只有经过身份验证,可以从State2和3(重新)Assoc请求/响应/解除关联

Class3 Frames:只有在Assoc请求下,可以能在State3内解除认证

每个State都可以进行fuzzing,但是第一个肯定是比后两个容易,因为后面两个都提到了需要成功认证的协议。

 

(两个图,第一个详细点,那个能看懂就看那个就行了)

 

 

一、Access Points Fuzzing 802.11 的介绍:

 

 


1、Fuzzing原理

无线协议里面,Beacon是802.11里面的一个重要组成部分,它涵盖了几乎所有AP的重要配置信息。

下面举个ApFuzzing框架的例子,就是通过构建畸形数据包,并将针对Wi-Fi 设备,然后发现已知和未知的漏洞。

 

Fuzzing802.11接入点,类似于802.11 client的fuzzing。无线客户端功能由接入点解析

802.11个访问点栈将解析大量的802.11的数据包:

   *Probe request

   *Authentication requests

   *Association requests

   *Crypted and unencrypted data frames

   *Control frames

 

还有一些其他协议接入点可以fuzzing:

WPA/WPA2 handshakes

基于EAP的认证

基于State的Fuzzing

 

State1进行成功的身份验证请求

State2 进行成功的关联请求

State3是基于认证成功的密钥交换

技术分享

 

2.Fuzzing 举例

Scapy是一个开放源代码的网络编程语言,它是基于Python,可以重放数据,捕获分析,产生随机数据包。可以根据自己的测试需求去改变,好像好多东西都需要它的支持,用处挺大的。有人基于Scapy写了一个叫做wifuzz的工具,写的接入点挺全的,能够fuzz出一些堆栈错误或者Crash,支持的接入点:

技术分享

 

我选了个auth 模式的FUZZ,它会将FUZZ出来的的数据保存到一个路径下供我们查看。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ sudo python wifuzz.py -s fuzztest auth
Thur Sep 26 21:41:36 2016 {MAIN} Target SSID: fuzztest; Interface: wlan0; Ping timeout:
60;PCAP directory: /dev/shm; Test mode? False; Fuzzer(s): auth;
Thur Sep 26 21:41:36 2016 {WIFI} Waiting for a beacon from SSID=[fuzztest] Thur Sep 26 21:41:36 2016 {WIFI} Beacon from SSID=[fuzztest] found (MAC=[11:22:33:44:55:66])
Thur Sep 26 21:41:36 2016 {WIFI} Starting fuzz ‘auth‘
Thur Sep 26 21:41:36 2016 {WIFI} [R00001] Sending packets 1-100
Thur Sep 26 21:41:50 2016 {WIFI} [R00001] Checking if the AP is still up...
Thur Sep 26 21:41:50 2016 {WIFI} Waiting for a beacon from SSID=[fuzztest] Thur Sep 26 21:41:50 2016 {WIFI} Beacon from SSID=[fuzztest] found (MAC=[11:22:33:44:55:66])
Thur Sep 26 21:41:50 2016 {WIFI} [R00002] Sending packets 101-200 Thur Sep 26 21:42:04 2016 {WIFI} [R00002] Checking if the AP is still up...
Thur Sep 26 21:42:04 2016 {WIFI} Waiting for a beacon from SSID=[fuzztest]
Thur Sep 26 21:42:04 2016 {WIFI} Beacon from SSID=[fuzztest] found (MAC=[11:22:33:44:55:66])
Thur Sep 26 21:42:04 2016 {WIFI} [R00003] Sending packets 201-300 Thur Sep 26 21:42:18 2016 {WIFI} [R00003] Checking if the AP is still up...
Thur Sep 26 21:42:18 2016 {WIFI} Waiting for a beacon from SSID=[fuzztest] Thur Sep 26 21:42:19 2016 {WIFI} Beacon from SSID=[fuzztest] found (MAC=[11:22:33:44:55:66])
Thur Sep 26 21:42:19 2016 {WIFI} [R00004] Sending packets 301-400
Thur Sep 26 21:42:42 2016 {WIFI} [R00004] recv() timeout exceeded! (packet #325) Thur Sep 26 21:42:42 2016 {WIFI} [R00004] Checking if the AP is still up...
Thur Sep 26 21:42:42 2016 {WIFI} Waiting for a beacon from SSID=[fuzztest]
Thur Sep 26 10:40:42 2016 {WIFI} [!] The AP does not respond anymore. Latest test-case has been written to ‘/dev/shm/wifuzz-69erb.pcap‘

 

再来个Beacon的内容配置:

技术分享

 

在Metasploit中呢,有一个专门用于fuzz Beacon的模块,不过要自己安装Lorcon2这个无线注入模块。https://github.com/gitpan/Net-Lorcon2  安装后要配置好环境变量,这时候容易出错,细心点就行了。

技术分享

 

因为是 Beacon这个接入点的FUZZ,那么它会产生无规则信息。

技术分享

 

这就是个简单的举例,大家读懂了协议,Fuzzer都可以自己来。

 

二、基于802.11 Driver 的Fuzzing

 


 

关于802.11协议标准的深入解析

(1)802.11扩展有点复杂的...

    几种帧类型(管理,数据,控制)大量的信令

    速率,信道,网络名称,密码

    所有这些东西都必须由固件/驱动程序解析

    单独的针对驱动进行闭源驱动、逆向,开源驱动,黑白审计什么的,会很难....也很费劲,所以,Fuzz是一种很不错的选择

(2)Madwifi:是个Linux下的无线驱动,例如Atheros芯片驱动(没搞过无线的应该不知道不同芯片之间有啥区别,google一下就行了)

(3)802.11芯片提供了几种模式的操作用处:

     监听802.11层

     作为AP接入点

     作为一个Ad-Hoc网络

     作为一个Station

 (4)802.11一共有两个扫描技术(主动与被动)

     主动扫描:发送探测请求,并监听探测响应

     被动扫描:监听Beacon和信道跳跃

    (驱动可以监听Beacon和探测的响应数据。)

  (5)802.11的扫描技术(主动与被动)

     主动扫描:发送探测请求,并监听探测响应

     被动扫描:监听Beacon和信道跳跃

     驱动可以监听Beacon和探测的响应数据

 

1、Fuzzing原理及Fuzzer

 

关于information elements

信息元素是management frames.中的必要字段,信息元素是由类型、长度和值组成的。

信息元素由一个8位的类型字段,一个8位的长度字段,和多达255个字节的数据。 这种类型的结构是非常类似于在许多不同的协议中使用的普通类型-长度-值(TLV)形式。 信标和探测响应分组必须包含一个SSID 信息元素,对于大多数无线客户端处理数据包。一个支持信息元素值和信道信息元素。

技术分享

 

技术分享

 

拿这个老外的例子来说吧:

类型的元素为(1byte)

长度是Payload总长度的值(1byte)

Payload的信息元素值为(0-255byte)

 

技术分享

技术分享

 

有一些信息值是有固定长度的,如果不正确可能缓冲区溢出,如果在802.11内长度高于buffer的大小,则可能出现溢出。

例如。SSID属性,0为它的最小值,最大为32byte

用个循环来表示:

1
2
For SSID, test fuzz ssid {0, 1, MIN-1, MIN, MIN+1, MAX-1, MAX, MAX+1,
254, 255} length

长度值可能是有用的,以触发缓冲区溢出,如果不仔细检查的执行过程中的分析过程中的 802.11帧。这些信息大部分元素最小、固定或极大值可以不同于字节边界(0-255byte)。

发送帧只有Beacon Frames的信息不一定是802.11中制定的元素,这个是为了测试Driver是否能在Beacon中解析有用无用的信息。

SSID的information elements Random

frame = Dot11( proto=0,FCfield=0,ID=0,addr1=DST,addr2=BSSID,

 

addr3=BSSID,SC=0,addr4=None)

 

/Dot11Beacon(beacon_interval=100,cap="ESS")

 

/Dot11Elt(ID=0)

 

sendp(fuzz(frame), loop=1)

 

老外的这一个框架,什么都明白了就。

 

 

 

必须要知道解析器通常检查哪些信息元素,了解底层协议那是肯定的.例如WPA的information elements

 

WPA IE (1 byte)

WPA OUI (3 bytes)

WPA TYPE (1 byte) + WPA VERSION (2 bytes)

WPA multicast cipher (4 bytes)

Number of unicast ciphers (2 bytes: m value)

WPA list of unicast ciphers (4*m bytes)

Number of authentication suites (2 bytes: n value)

WPA list of authentication suites (4 * n bytes)

当Fuzzer开始执行的时候,你可以用不同的“m”和“n”值来检查溢出?截断这些帧并且填充一些不相关的值来测试。

其实呢,我还是觉得,有了方法和思路,根据自己的Idea去Fuzzing比什么都要好,有个叫wifuzzit的框架:

https://github.com/bullo95/WiFi--/tree/master/wifuzzit

挺好的,也发现了一些溢出的CVE等:

 CVE-2008-1144年 :Marvell的驱动EAPOL-密钥长度溢出(无线AP)

 CVE-2007-5474 :Atheros的供应商特定信息元素溢出(无线AP)

CVE-2007-0933 :缓冲区溢出在无线驱动程序6.0.0.18的的D-Link DWL-G650 +(无线STA)

CVE-2007-5651 :可扩展身份验证协议漏洞(Cisco的无线AP与有线交换机)

CVE-2007-5475 :Marvell的驱动多个信息元素溢出(无线AP)

 

 

三、Windows上的Driver Vulnerabilities

 


其实关于Kernel的漏洞,只要你对Madwifi,无线协议比较清楚的话,再熟悉一些MIPS ,ARM指令就能看懂...注意是读懂,读懂不代表你也能挖出来。

 

 

差点把Google炸了才翻出一篇比较老的文章,专门写kernel的,我把主要地方给翻译下贴过来吧,前面讲协议的我看得懂可以分析下,这个后面涉及到内核的我也分析不了,做二进制的有这方面的需求的,可以看下:

 

最开始他也是提了802.11 Driver 的State,也是说根据State1Fuzzing来的思路,前面噼里啪啦说的跟我前面一样,我直接就贴他后面的漏洞分析。

 

一个DWL-G132 USB  A5AGU.SYS在WINxp下的测试:结果是造成内核崩溃

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
DRIVER_IRQL_NOT_LESS_OR_EQUAL (d1)
An attempt was made to access a pageable (or completely invalid) address at an
interrupt request level (IRQL) that is too high.  This is usually
caused by drivers using improper addresses.
If kernel debugger is available get stack backtrace.
Arguments:
Arg1: 56149a1b, memory referenced
Arg2: 00000002, IRQL
Arg3: 00000000, value 0 = read operation, 1 = write operation
Arg4: 56149a1b, address which referenced memory
 
ErrCode = 00000000
eax=00000000 ebx=82103ce0 ecx=00000002 edx=82864dd0 esi=f24105dc edi=8263b7a6
eip=56149a1b esp=80550658 ebp=82015000 iopl=0         nv up ei ng nz ac pe nc
cs=0008  ss=0010  ds=0023  es=0023  fs=0030  gs=0000             efl=00010296
56149a1b ??              ???
Resetting default scope
 
LAST_CONTROL_TRANSFER:  from 56149a1b to 804e2158
 
FAILED_INSTRUCTION_ADDRESS: 
+56149a1b
56149a1b ??              ???
 
STACK_TEXT:  
805505e4 56149a1b badb0d00 82864dd0 00000000 nt!KiTrap0E+0x233
80550654 82015000 82103ce0 81f15e10 8263b79c 0x56149a1b
80550664 f2408d54 81f15e10 82103c00 82015000 0x82015000
80550694 f24019cc 82015000 82103ce0 82015000 A5AGU+0x28d54
805506b8 f2413540 824ff008 0000000b 82015000 A5AGU+0x219cc
805506d8 f2414fae 824ff008 0000000b 0000000c A5AGU+0x33540
805506f4 f24146ae f241d328 8263b760 81f75000 A5AGU+0x34fae
80550704 f2417197 824ff008 00000001 8263b760 A5AGU+0x346ae
80550728 804e42cc 00000000 821f0008 00000000 A5AGU+0x37197
80550758 f74acee5 821f0008 822650a8 829fb028 nt!IopfCompleteRequest+0xa2
805507c0 f74adb57 8295a258 00000000 829fb7d8 USBPORT!USBPORT_CompleteTransfer+0x373
805507f0 f74ae754 026e6f44 829fb0e0 829fb0e0 USBPORT!USBPORT_DoneTransfer+0x137
80550828 f74aff6a 829fb028 804e3579 829fb230 USBPORT!USBPORT_FlushDoneTransferList+0x16c
80550854 f74bdfb0 829fb028 804e3579 829fb028 USBPORT!USBPORT_DpcWorker+0x224
80550890 f74be128 829fb028 00000001 80559580 USBPORT!USBPORT_IsrDpcWorker+0x37e
805508ac 804dc179 829fb64c 6b755044 00000000 USBPORT!USBPORT_IsrDpc+0x166
805508d0 804dc0ed 00000000 0000000e 00000000 nt!KiRetireDpcList+0x46
805508d4 00000000 0000000e 00000000 00000000 nt!KiIdleLoop+0x26

Fuzz的五秒钟已产生一个缺陷,已经可能对指针进行控制。 为了执行任意代码,然而,一个恶意框架必须定位。在这种情况下,在EDI寄存器所指向成一样的方式,它在Broadcom漏洞做了帧的源地址字段。 随机生成信息元素之一-假EIP值到源地址。

1
2
3
4
5
6
kd> dd 0x8263b7a6 (edi)
8263b7a6  f3793ee8 3ee8a34e a34ef379 6eb215f0
8263b7b6  fde19019 006431d8 9b001740 63594364
 
kd> s 0x8263b7a6 Lffff 0x1b 0x9a 0x14 0x56
8263bd2b  1b 9a 14 56 2a 85 56 63-00 55 0c 0f 63 6e 17 51  ...V*.Vc.U..cn.Q

下一步是确定哪些信息元素是Crash的原因。 解码后的内存Frame进行了一系列的修直到导致崩溃的特定信息元素被发现。 通过这种方法,确定了溢出的存在利用这个漏洞涉及发现内存中的返回地址指向一个JMP EDI,EDI ,或推电子数据交换; ret指令序列。这是通过运行msfpescan应用程序中包含的ntoskrnl Metasploit框架。 所得的地址必须被调整以考虑内核的基址。ntoskrnl.exe中的地址是0x804f16eb(0x800d7000 + 0x0041a6eb)

1
2
3
4
5
6
$ msfpescan ntoskrnl.exe -j edi
[ntoskrnl.exe]
0x0040365d push edi; retn 0x0001
0x00405aab call edi
0x00409d56 push edi; ret
0x0041a6eb jmp edi

 

实在找不出比这更新的研究内容了,关于国外针对802.11 Fuzz的研究也停滞在了11年。阅读了大约30多份洋码子PDF与PPT以及学术论文,综合写下,有点辛苦.....不管研究不研究,只想让大家明白存在这么一种技术,它的原理是这样,也希望在中文引擎的搜索上,出现关于这一技术的研究内容,如果你也恰好研究过,发现了那里不妥或者出现了错误或者是有更好的方法,欢迎交流指正。上来就喷的,你也放心,我一定会给你喷回去的。

 

以上是关于Fuzzing101-Exercise1 fuzz xpdf CVE-2019-13288的主要内容,如果未能解决你的问题,请参考以下文章

第一次ActiveX Fuzzing测试

资源:开源Fuzzers工具列表 (以及其它fuzzing工具)

Go Fuzzing已经进入Beta测试阶段

Kitty:Python语言编写的Fuzzing框架

工具分享DAFF:Android应用程序Fuzzing框架

XSS Fuzzing初探