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