CVE-2019-9200: Heap based Buffer Underwrite in ImageStream::getLine() – poppler 0.74.0

Heap based Buffer Underwrite in ImageStream::getLine() – poppler 0.74.0

Loginsoft-2019-1098

26 February, 2019

CVE Number

CVE-2019-9200

CWE

CWE-124: Buffer Underwrite.

Product Details

Poppler is a free software utility library for rendering Portable Document Format documents.
URL: https://gitlab.freedesktop.org/poppler/poppler/

Vulnerable Versions

0.74.0

Vulnerability Details

During our research there is a Heap based Buffer Underwrite in ImageStream::getLine() located at Stream.cc in poppler 0.74.0. The same be triggered by sending a crafted pdf file to the pdfimages binary. It allows an attacker to cause Denial of Service (Segmentation fault) or possibly have unspecified other impact.

SYNOPSIS

When a crafted pdf file is passed to the binary, we observed in the function ImageOutputDev::writeImage() at line writeImageFile(writer, format,format == imgRGB ? “ppm” : “pbm”,str, width, height, colorMap); here writeImageFile calls to another function ImageOutputDev::writeImageFile() here at the line p = imgStr->getLine(); now this line invokes to another function ImageStream::getLine(), here in the line for ( ; readChars < inputLineSize; readChars++) inputLine[readChars] = EOF; where readchars contains a negative value which is readChars=-0x1. So, when inputline[readChars]=EOF,readChar consists of negative value in which EOF is passed, triggering a heap based underwrite vulnerability.

vulnerable Source code
if (unlikely(inputLine == nullptr)) {
return nullptr;
}
int readChars = str->doGetChars(inputLineSize, inputLine);
for ( ; readChars < inputLineSize; readChars++) inputLine[readChars] = EOF;
if (nBits == 1) {
unsigned char *p = inputLine;
for (int i = 0; i < nVals; i += 8) {
const int c = *p++;
Analysis

DEBUG:
ASAN OUTPUT:

=================================================================
==5771==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000010b3f at pc 0x7ffff67e3c20 bp 0x7fffffffa9d0 sp 0x7fffffffa9c0
WRITE of size 1 at 0x603000010b3f thread T0
#0 0x7ffff67e3c1f in ImageStream::getLine() /home/aceteam/Desktop/packages/poppler-master/poppler/Stream.cc:499
#1 0x55555556334a in ImageOutputDev::writeImageFile(ImgWriter*, ImageOutputDev::ImageFormat, char const*, Stream*, int, int, GfxImageColorMap*) /home/aceteam/Desktop/packages/poppler-master/utils/ImageOutputDev.cc:410
#2 0x555555565939 in ImageOutputDev::writeImage(GfxState*, Object*, Stream*, int, int, GfxImageColorMap*, bool) /home/aceteam/Desktop/packages/poppler-master/utils/ImageOutputDev.cc:666
#3 0x555555565b95 in ImageOutputDev::drawImage(GfxState*, Object*, Stream*, int, int, GfxImageColorMap*, bool, int*, bool) /home/aceteam/Desktop/packages/poppler-master/utils/ImageOutputDev.cc:702
#4 0x7ffff6667e1b in Gfx::doImage(Object*, Stream*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:4593
#5 0x7ffff666b5f9 in Gfx::opBeginImage(Object*, int) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:4897
#6 0x7ffff6636a48 in Gfx::execOp(Object*, Object*, int) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:876
#7 0x7ffff6635b3e in Gfx::go(bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:752
#8 0x7ffff6635590 in Gfx::display(Object*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:714
#9 0x7ffff666b09e in Gfx::drawForm(Object*, Dict*, double const*, double const*, bool, bool, GfxColorSpace*, bool, bool, bool, Function*, GfxColor*) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:4841
#10 0x7ffff6669ca4 in Gfx::doForm(Object*) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:4764
#11 0x7ffff6662f75 in Gfx::opXObject(Object*, int) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:4181
#12 0x7ffff6636a48 in Gfx::execOp(Object*, Object*, int) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:876
#13 0x7ffff6635b3e in Gfx::go(bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:752
#14 0x7ffff6635590 in Gfx::display(Object*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:714
#15 0x7ffff6762e8d in Page::displaySlice(OutputDev*, double, double, int, bool, bool, int, int, int, int, bool, bool (*)(void*), void*, bool (*)(Annot*, void*), void*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Page.cc:548
#16 0x7ffff6762354 in Page::display(OutputDev*, double, double, int, bool, bool, bool, bool (*)(void*), void*, bool (*)(Annot*, void*), void*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Page.cc:469
#17 0x7ffff676dac9 in PDFDoc::displayPage(OutputDev*, int, double, double, int, bool, bool, bool, bool (*)(void*), void*, bool (*)(Annot*, void*), void*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/PDFDoc.cc:633
#18 0x7ffff676db63 in PDFDoc::displayPages(OutputDev*, int, int, double, double, int, bool, bool, bool, bool (*)(void*), void*, bool (*)(Annot*, void*), void*) /home/aceteam/Desktop/packages/poppler-master/poppler/PDFDoc.cc:650
#19 0x555555560c93 in main /home/aceteam/Desktop/packages/poppler-master/utils/pdfimages.cc:220
#20 0x7ffff5abeb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)
#21 0x55555555f129 in _start (/usr/local/bin/pdfimages+0xb129)


0x603000010b3f is located 1 bytes to the left of 30-byte region [0x603000010b40,0x603000010b5e)
allocated by thread T0 here:
#0 0x7ffff6ef8b50 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb50)
#1 0x555555565ed4 in gmalloc(unsigned long, bool) /home/aceteam/Desktop/packages/poppler-master/goo/gmem.h:41
#2 0x5555555660f2 in gmallocn(int, int, bool) (/usr/local/bin/pdfimages+0x120f2)
#3 0x7ffff662f66b in gmallocn_checkoverflow(int, int) /home/aceteam/Desktop/packages/poppler-master/goo/gmem.h:119
#4 0x7ffff67e33a3 in ImageStream::ImageStream(Stream*, int, int, int) /home/aceteam/Desktop/packages/poppler-master/poppler/Stream.cc:446
#5 0x555555563175 in ImageOutputDev::writeImageFile(ImgWriter*, ImageOutputDev::ImageFormat, char const*, Stream*, int, int, GfxImageColorMap*) /home/aceteam/Desktop/packages/poppler-master/utils/ImageOutputDev.cc:381
#6 0x555555565939 in ImageOutputDev::writeImage(GfxState*, Object*, Stream*, int, int, GfxImageColorMap*, bool) /home/aceteam/Desktop/packages/poppler-master/utils/ImageOutputDev.cc:666
#7 0x555555565b95 in ImageOutputDev::drawImage(GfxState*, Object*, Stream*, int, int, GfxImageColorMap*, bool, int*, bool) /home/aceteam/Desktop/packages/poppler-master/utils/ImageOutputDev.cc:702
#8 0x7ffff6667e1b in Gfx::doImage(Object*, Stream*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:4593
#9 0x7ffff666b5f9 in Gfx::opBeginImage(Object*, int) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:4897
#10 0x7ffff6636a48 in Gfx::execOp(Object*, Object*, int) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:876
#11 0x7ffff6635b3e in Gfx::go(bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:752
#12 0x7ffff6635590 in Gfx::display(Object*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:714
#13 0x7ffff666b09e in Gfx::drawForm(Object*, Dict*, double const*, double const*, bool, bool, GfxColorSpace*, bool, bool, bool, Function*, GfxColor*) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:4841
#14 0x7ffff6669ca4 in Gfx::doForm(Object*) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:4764
#15 0x7ffff6662f75 in Gfx::opXObject(Object*, int) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:4181
#16 0x7ffff6636a48 in Gfx::execOp(Object*, Object*, int) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:876
#17 0x7ffff6635b3e in Gfx::go(bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:752
#18 0x7ffff6635590 in Gfx::display(Object*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Gfx.cc:714
#19 0x7ffff6762e8d in Page::displaySlice(OutputDev*, double, double, int, bool, bool, int, int, int, int, bool, bool (*)(void*), void*, bool (*)(Annot*, void*), void*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Page.cc:548
#20 0x7ffff6762354 in Page::display(OutputDev*, double, double, int, bool, bool, bool, bool (*)(void*), void*, bool (*)(Annot*, void*), void*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/Page.cc:469
#21 0x7ffff676dac9 in PDFDoc::displayPage(OutputDev*, int, double, double, int, bool, bool, bool, bool (*)(void*), void*, bool (*)(Annot*, void*), void*, bool) /home/aceteam/Desktop/packages/poppler-master/poppler/PDFDoc.cc:633
#22 0x7ffff676db63 in PDFDoc::displayPages(OutputDev*, int, int, double, double, int, bool, bool, bool, bool (*)(void*), void*, bool (*)(Annot*, void*), void*) /home/aceteam/Desktop/packages/poppler-master/poppler/PDFDoc.cc:650
#23 0x555555560c93 in main /home/aceteam/Desktop/packages/poppler-master/utils/pdfimages.cc:220
#24 0x7ffff5abeb96 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21b96)


SUMMARY: AddressSanitizer: heap-buffer-overflow /home/aceteam/Desktop/packages/poppler-master/poppler/Stream.cc:499 in ImageStream::getLine()
Shadow bytes around the buggy address:
0x0c067fffa110: 00 fa fa fa 00 00 00 fa fa fa fd fd fd fa fa fa
0x0c067fffa120: 00 00 00 fa fa fa 00 00 00 fa fa fa fd fd fd fa
0x0c067fffa130: fa fa fd fd fd fa fa fa fd fd fd fd fa fa 00 00
0x0c067fffa140: 00 fa fa fa 00 00 00 fa fa fa 00 00 00 fa fa fa
0x0c067fffa150: 00 00 00 fa fa fa 00 00 00 fa fa fa fd fd fd fd
=>0x0c067fffa160: fa fa 00 00 00 fa fa[fa]00 00 00 06 fa fa fa fa
0x0c067fffa170: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fffa180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fffa190: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fffa1a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fffa1b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07 
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==5771==ABORTING

GDB :

for ( ; readChars < inputLineSize; readChars++) inputLine[readChars] = EOF;
[ Legend: Modified register | Code | Heap | Stack | String ]
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ registers ]────
$rax : 0xffffffff → 0x0000000000000000
$rbx : 0x7fffffffac50 → 0x00007fffffffac90 → 0x000000000000000a
$rcx : 0x0 
$rdx : 0x0 
$rsp : 0x7fffffffa9e0 → 0x00007fffffffaa30 → 0x00007fffffffac80 → 0x00007fffffffad40 → 0x00007fffffffad90 → 0x00007fffffffc380 → 0x00007fffffffc3c0 → 0x00007fffffffc420
$rbp : 0x7fffffffaa30 → 0x00007fffffffac80 → 0x00007fffffffad40 → 0x00007fffffffad90 → 0x00007fffffffc380 → 0x00007fffffffc3c0 → 0x00007fffffffc420 → 0x00007fffffffc850
$rsi : 0x3 
$rdi : 0x608000002120 → 0x00007ffff6d150a8 → 0x00007ffff67e8d36 →  push rbp
$rip : 0x7ffff67e3b77 →  mov rax, QWORD PTR [rbp-0x48]
$r8 : 0x3 
$r9 : 0x2 
$r10 : 0x7fffffffa1d8 → 0x00007ffff7fa8000 → 0x00007ffff7fde000 → 0x00007ffff716a698 → 0x00007ffff6f09090 → repz ret
$r11 : 0x7fffffffa1d8 → 0x00007ffff7fa8000 → 0x00007ffff7fde000 → 0x00007ffff716a698 → 0x00007ffff6f09090 → repz ret
$r12 : 0xffffffff55a → 0x0000000000000000
$r13 : 0x7fffffffaad0 → 0x0000000041b58ab3
$r14 : 0x6060000019a0 → 0x0000608000002120 → 0x00007ffff6d150a8 → 0x00007ffff67e8d36 →  push rbp
$r15 : 0x3 
$eflags: [ZERO carry PARITY adjust sign trap INTERRUPT direction overflow resume virtualx86 identification]
$ss: 0x002b $gs: 0x0000 $fs: 0x0000 $es: 0x0000 $cs: 0x0033 $ds: 0x0000 
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ stack ]────
0x00007fffffffa9e0│+0x00: 0x00007fffffffaa30 → 0x00007fffffffac80 → 0x00007fffffffad40 → 0x00007fffffffad90 → 0x00007fffffffc380 → 0x00007fffffffc3c0 → 0x00007fffffffc420 ← $rsp
0x00007fffffffa9e8│+0x08: 0x00006060000019a0 → 0x0000608000002120 → 0x00007ffff6d150a8 → 0x00007ffff67e8d36 →  push rbp
0x00007fffffffa9f0│+0x10: 0x00007fffffffffff
0x00007fffffffa9f8│+0x18: 0x0e8cb3deecb26000
0x00007fffffffaa00│+0x20: 0x00000008ffffae50 → 0x0000000000000000
0x00007fffffffaa08│+0x28: 0x0e8cb3deecb26000
0x00007fffffffaa10│+0x30: 0x00000ffffffff55a → 0x0000000000000000
0x00007fffffffaa18│+0x38: 0x00007fffffffac50 → 0x00007fffffffac90 → 0x000000000000000a
────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ code:i386:x86-64 ]────
0x7ffff67e3b68  sbb BYTE PTR [rcx-0x3076b73a], cl
0x7ffff67e3b6f  call 0x7ffff64e7dd0 
0x7ffff67e3b74  mov DWORD PTR [rbp-0x40], eax
→ 0x7ffff67e3b77  mov rax, QWORD PTR [rbp-0x48]
0x7ffff67e3b7b  add rax, 0x18
0x7ffff67e3b7f  mov rdx, rax
0x7ffff67e3b82  mov rax, rdx
0x7ffff67e3b85  shr rax, 0x3
0x7ffff67e3b89  add rax, 0x7fff8000
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ source:/home/aceteam/Desktop/packages/poppler-master/poppler/Stream.cc+499 ]────
494 if (unlikely(inputLine == nullptr)) {
495 return nullptr;
496 }
497 
498 int readChars = str->doGetChars(inputLineSize, inputLine);
// readChars=-0x1
→ 499 for ( ; readChars < inputLineSize; readChars++) inputLine[readChars] = EOF;
500 if (nBits == 1) {
501 unsigned char *p = inputLine;
502 for (int i = 0; i > 7) & 1);
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ threads ]────
[#0] Id 1, Name: "pdfimages", stopped, reason: BREAKPOINT
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────[ trace ]────
[#0] 0x7ffff67e3b77 → Name: ImageStream::getLine(this=0x6060000019a0)
[#1] 0x55555556334b → Name: ImageOutputDev::writeImageFile(this=0x60f000000040, writer=0x603000010b10, format=ImageOutputDev::imgRGB, ext=0x55555556b8c0 "ppm", str=0x608000002120, width=0xa, height=0xa, colorMap=0x7fffffffbe70)
[#2] 0x55555556593a → Name: ImageOutputDev::writeImage(this=0x60f000000040, state=0x618000002080, ref=0x0, str=0x608000002120, width=0xa, height=0xa, colorMap=0x7fffffffbe70, inlineImg=0x1)
[#3] 0x555555565b96 → Name: ImageOutputDev::drawImage(this=0x60f000000040, state=0x618000002080, ref=0x0, str=0x608000002120, width=0xa, height=0xa, colorMap=0x7fffffffbe70, interpolate=0x0, maskColors=0x0, inlineImg=0x1)
[#4] 0x7ffff6667e1c → Name: Gfx::doImage(this=0x612000000640, ref=0x0, str=0x608000002120, inlineImg=0x1)
[#5] 0x7ffff666b5fa → Name: Gfx::opBeginImage(this=0x612000000640, args=0x7fffffffc5e0, numArgs=0x0)
[#6] 0x7ffff6636a49 → Name: Gfx::execOp(this=0x612000000640, cmd=0x7fffffffc4e0, args=0x7fffffffc5e0, numArgs=0x0)
[#7] 0x7ffff6635b3f → Name: Gfx::go(this=0x612000000640, topLevel=0x0)
[#8] 0x7ffff6635591 → Name: Gfx::display(this=0x612000000640, obj=0x7fffffffd070, topLevel=0x0)
[#9] 0x7ffff666b09f → Name: Gfx::drawForm(this=0x612000000640, str=0x7fffffffd070, resDict=0x6080000019a0, matrix=0x7fffffffce70, bbox=0x7fffffffce30, transpGroup=0x1, softMask=0x0, blendingColorSpace=0x0, isolated=0x0, knockout=0x0, alpha=0x0, transferFunc=0x0, backdropColor=0x0)
────────────────────────────────────────────────────────

gef➤ p/d readChars 
$21 = -1
Proof of Concept

: pdfimages -f 1 -l 1 -opw testing -upw testing -j -p -q $POC output
*This can be triggerd when compiled with CFLAGS=”-fsanitize=address -g -O0″
Vendor Disclosure: 2019-2-25
Public Disclosure: 2019-3-2

Credit

Discovered by ACE Team – Loginsoft