CVE-2019-13288 in XPDF 3.02
build:
build.sh:
#!/bin/sh
CC=afl-clang-fast CXX=afl-clang-fast++ ./configure --prefix=$PWD/bd
make
fuzzing:
start.sh:
#!/bin/sh
afl-fuzz -i $PWD/ipt -o $PWD/opt -s 123 -m none -- $PWD/bd/bin/pdftotext @@ $PWD/output
we get crash below :
american fuzzy lop ++4.09a {default} (.../xpdf-3.02/bd/bin/pdftotext) [fast]
┌─ process timing ────────────────────────────────────┬─ overall results ────┐
│ run time : 0 days, 0 hrs, 48 min, 47 sec │ cycles done : 0 │
│ last new find : 0 days, 0 hrs, 0 min, 4 sec │ corpus count : 782 │
│last saved crash : 0 days, 0 hrs, 7 min, 51 sec │saved crashes : 5 │
│ last saved hang : 0 days, 0 hrs, 4 min, 29 sec │ saved hangs : 2 │
├─ cycle progress ─────────────────────┬─ map coverage┴──────────────────────┤
│ now processing : 781.0 (99.9%) │ map density : 2.81% / 4.58% │
│ runs timed out : 0 (0.00%) │ count coverage : 4.10 bits/tuple │
├─ stage progress ─────────────────────┼─ findings in depth ─────────────────┤
│ now trying : trim 64/64 │ favored items : 63 (8.06%) │
│ stage execs : 47/384 (12.24%) │ new edges on : 138 (17.65%) │
│ total execs : 1.10M │ total crashes : 5 (5 saved) │
│ exec speed : 321.8/sec │ total tmouts : 37 (0 saved) │
├─ fuzzing strategy yields ────────────┴─────────────┬─ item geometry ───────┤
│ bit flips : disabled (default, enable with -D) │ levels : 21 │
│ byte flips : disabled (default, enable with -D) │ pending : 557 │
│ arithmetics : disabled (default, enable with -D) │ pend fav : 2 │
│ known ints : disabled (default, enable with -D) │ own finds : 780 │
│ dictionary : n/a │ imported : 0 │
│havoc/splice : 572/451k, 213/333k │ stability : 100.00% │
│py/custom/rq : unused, unused, unused, unused ├───────────────────────┘
│ trim/eff : 3.39%/310k, disabled │ [cpu000: 6%]
└─ strategy: explore ────────── state: in progress ──┘
analyze:
use tmin to minimize the crashes
#!/bin/sh
mkdir -p $PWD/opt/default/minimized
for i in $PWD/opt/default/crashes/id* ; do afl-tmin -i $i -o $PWD/opt/default/minimized/`basename $i` -- $PWD/bd/bin/pdftotext @@ $PWD/output ; done
rebuild with dbg symbols and feed the crash sample
gdb dbg:
gdb --args ./pdftotext id:000000,sig:11,src:000499,time:429922,execs:198634,op:havoc,rep:4 ./1
start
step to the crash point and look up backtrace , we entered a infinete loop at src Parser.cc:94
[#0] Id 1, Name: "pdftotext", stopped 0x7ffff7af1e57 in _int_malloc (), reason: SIGSEGV
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── trace ────
[#0] 0x7ffff7af1e57 → _int_malloc(av=0x7ffff7c68c80 <main_arena>, bytes=0x158)
[#1] 0x7ffff7af3262 → __GI___libc_malloc(bytes=0x158)
[#2] 0x7ffff7e2c98c → operator new(unsigned long)()
[#3] 0x5555555e2ca3 → FileStream::makeSubStream(this=0x555555693c00, startA=0xa, limitedA=0x0, lengthA=0x0, dictA=0x7fffff7ff130)
[#4] 0x5555555f7a7c → XRef::fetch(this=0x555555695230, num=0x4, gen=0x0, obj=0x7fffff7ff1e0)
[#5] 0x5555555e016f → Object::dictLookup(obj=0x7fffff7ff1e0, key=0x55555560c1c6 "Length", this=0x7fffff7ff3a0)
[#6] 0x5555555e016f → Parser::makeStream(this=0x555556bc5a60, dict=0x7fffff7ff3a0, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0x0, objNum=0x4, objGen=0x0)
[#7] 0x5555555e071c → Parser::getObj(this=0x555556bc5a60, obj=0x7fffff7ff3a0, fileKey=0x0, encAlgorithm=cryptRC4, keyLength=0x0, objNum=0x4, objGen=0x0)
[#8] 0x5555555f7bbd → XRef::fetch(this=0x555555695230, num=0x4, gen=0x0, obj=0x7fffff7ff3a0)
[#9] 0x5555555e016f → Object::dictLookup(obj=0x7fffff7ff3a0, key=0x55555560c1c6 "Length", this=0x7fffff7ff560)
here causes a recursion, will finally run out of stack memory
mitigation:
#define recursionLimit 500
and exit after reaching the limit .