Talos Vulnerability Report

TALOS-2016-0174

Google Chrome PDFium jpeg2000 SIZ Code Execution Vulnerability

June 8, 2016

Report ID

CVE-2016-1681

Summary

An exploitable heap buffer overflow vulnerability exists in the Pdfium PDF reader included in the Google Chrome web browser. A specially crafted PDF document with embedded jpeg2000 image can cause a heap buffer overflow potentially resulting in an arbitrary code execution. An attacker can serve the malicious PDF file on a website and wait for a victim to visit to trigger this vulnerability.

Tested Versions

Google Chrome 50.0.2661.94

Pdfium Git 2016-05-08

Product URLs

https://www.google.com/chrome/browser/desktop/

CVSSv3 Score

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

Details

A heap buffer overflow vulnerability is present in the jpeg2000 image parser library as used by the Chrome’s PDF renderer, PDFium. The vulnerability is located in the underlying jpeg2000 parsing library, openjpeg, but is made exploitable in case of Chrome due to special build process.

Namely, an existing assert call in the openjpeg library usually prevents the heap overflow from being reached, but in the release versions of Chrome the assertations are omited. The source of the vulnerability is located in the following code in function opj_j2k_read_siz in j2k.c file:

```
for     (i = 0; i < l_nb_tiles; ++i) {
        l_current_tile_param->tccps = (opj_tccp_t*) opj_calloc(l_image->numcomps, sizeof(opj_tccp_t));
        if (l_current_tile_param->tccps == 00) {
                opj_event_msg(p_manager, EVT_ERROR, "Not enough memory to take in charge SIZ marker     \n");
                return OPJ_FALSE;
        }

        ++l_current_tile_param;
}
```

If in the above call to opj_calloc, which is a calloc wrapper, numcomps value happens to be zero, calloc will return a unique pointer which can be later passed to free (this is implementation dependent, but is so on modern Linux OSes). The unique pointer returned by calloc will usually be a small allocation (0x20 bytes in case of x64 code). This can lead to a heap buffer overflow later in the code when this buffer is being used. The overflow happens inside opj_j2k_read_SQcd_SQcc function where previously allocated buffer is being dereferenced. The first out of bounds memory write happens in the following code:

```
l_tccp->qntsty = l_tmp & 0x1f;
l_tccp->numgbits = l_tmp >> 5;
```

In the above code, l_tccp pointer will be pointing to the previously erroneously allocated area. The same structure is dereferenced during further out of bounds writes in the following code.

First requirement for this overflow to happen, number of components to be 0, is actually checked against in an assert at the beginning of the function:

```
assert(p_comp_no <  p_j2k->m_private_image->numcomps);
```

If the required condition for the erroneous allocation is satisfied, the above assert would fail which indeed does happen in the default build of openjpeg library. But, since the release builds of Chrome and PDFium omit these asserts the point of buffer overflow can be reached. The attached jpeg2000 testcase (embedded inside a PDF) has it’s SIZ marker truncated (SIZ marker begins with 0xFF51). Number of components specified in the SIZ marker is 0 and isn’t followed by individual component information. This short circuits the code that is parsing the file in opj_j2k_read_siz and leads to the required erroneous call to calloc. The only difference between a valid jpeg2000 file and the one that triggers this vulnerability is the fact that SIZ marker specifies 0 components.

Crash Information

For debugging purposes, both a standard and ASAN build of latest PDFium code were tested, resulting in following crashes.

Regular build crashes due to heap corruption. A heap buffer overflow has resulted in adjacent heap chunk metadata overwrite:

```
Rendering PDF file min_chrome.pdf.
*** Error in `/home/test/pdfium_repo/pdfium_test_nonasan': free(): invalid next size (fast): 0x0000000000b947d0 ***
======= Backtrace: =========
/lib64/libc.so.6(+0x77da5)[0x7fdb6b408da5]
/lib64/libc.so.6(+0x804fa)[0x7fdb6b4114fa]
/lib64/libc.so.6(cfree+0x4c)[0x7fdb6b414cac]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4381a7]
/home/test/pdfium_repo/pdfium_test_nonasan[0x437bc0]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4408d5]
/home/test/pdfium_repo/pdfium_test_nonasan[0x442de9]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4fa2ba]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4b4640]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4b2b4c]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4b3a06]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4aea63]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4ae90f]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4b6308]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4afbc0]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4aee70]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4aba73]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4adddb]
/home/test/pdfium_repo/pdfium_test_nonasan[0x464071]
/home/test/pdfium_repo/pdfium_test_nonasan[0x463de3]
/home/test/pdfium_repo/pdfium_test_nonasan[0x40500a]
/home/test/pdfium_repo/pdfium_test_nonasan[0x40577e]
/home/test/pdfium_repo/pdfium_test_nonasan[0x405d32]
/lib64/libc.so.6(__libc_start_main+0xf0)[0x7fdb6b3b1580]
/home/test/pdfium_repo/pdfium_test_nonasan[0x4033d9]
======= Memory map: ========
00400000-0079d000 r-xp 00000000 fd:03 7867700                            /home/test/pdfium_repo/pdfium_test_nonasan
0079d000-007ad000 r--p 0039c000 fd:03 7867700                            /home/test/pdfium_repo/pdfium_test_nonasan
007ad000-007b1000 rw-p 003ac000 fd:03 7867700                            /home/test/pdfium_repo/pdfium_test_nonasan
00b7a000-00bac000 rw-p 00000000 00:00 0                                  [heap]
7fdb64000000-7fdb64021000 rw-p 00000000 00:00 0
7fdb64021000-7fdb68000000 ---p 00000000 00:00 0
7fdb6b17a000-7fdb6b190000 r-xp 00000000 fd:01 272875                     /usr/lib64/libgcc_s-5.3.1-20160406.so.1
7fdb6b190000-7fdb6b38f000 ---p 00016000 fd:01 272875                     /usr/lib64/libgcc_s-5.3.1-20160406.so.1
7fdb6b38f000-7fdb6b390000 r--p 00015000 fd:01 272875                     /usr/lib64/libgcc_s-5.3.1-20160406.so.1
7fdb6b390000-7fdb6b391000 rw-p 00016000 fd:01 272875                     /usr/lib64/libgcc_s-5.3.1-20160406.so.1
7fdb6b391000-7fdb6b548000 r-xp 00000000 fd:01 264380                     /usr/lib64/libc-2.22.so
7fdb6b548000-7fdb6b748000 ---p 001b7000 fd:01 264380                     /usr/lib64/libc-2.22.so
7fdb6b748000-7fdb6b74c000 r--p 001b7000 fd:01 264380                     /usr/lib64/libc-2.22.so
7fdb6b74c000-7fdb6b74e000 rw-p 001bb000 fd:01 264380                     /usr/lib64/libc-2.22.so
7fdb6b74e000-7fdb6b752000 rw-p 00000000 00:00 0
7fdb6b752000-7fdb6b76a000 r-xp 00000000 fd:01 264408                     /usr/lib64/libpthread-2.22.so
7fdb6b76a000-7fdb6b969000 ---p 00018000 fd:01 264408                     /usr/lib64/libpthread-2.22.so
7fdb6b969000-7fdb6b96a000 r--p 00017000 fd:01 264408                     /usr/lib64/libpthread-2.22.so
7fdb6b96a000-7fdb6b96b000 rw-p 00018000 fd:01 264408                     /usr/lib64/libpthread-2.22.so
7fdb6b96b000-7fdb6b96f000 rw-p 00000000 00:00 0
7fdb6b96f000-7fdb6ba70000 r-xp 00000000 fd:01 293829                     /usr/lib64/libm-2.22.so
7fdb6ba70000-7fdb6bc6f000 ---p 00101000 fd:01 293829                     /usr/lib64/libm-2.22.so
7fdb6bc6f000-7fdb6bc70000 r--p 00100000 fd:01 293829                     /usr/lib64/libm-2.22.so
7fdb6bc70000-7fdb6bc71000 rw-p 00101000 fd:01 293829                     /usr/lib64/libm-2.22.so
7fdb6bc71000-7fdb6bde3000 r-xp 00000000 fd:01 264431                     /usr/lib64/libstdc++.so.6.0.21
7fdb6bde3000-7fdb6bfe3000 ---p 00172000 fd:01 264431                     /usr/lib64/libstdc++.so.6.0.21
7fdb6bfe3000-7fdb6bfed000 r--p 00172000 fd:01 264431                     /usr/lib64/libstdc++.so.6.0.21
7fdb6bfed000-7fdb6bfef000 rw-p 0017c000 fd:01 264431                     /usr/lib64/libstdc++.so.6.0.21
7fdb6bfef000-7fdb6bff3000 rw-p 00000000 00:00 0
7fdb6bff3000-7fdb6c014000 r-xp 00000000 fd:01 264371                     /usr/lib64/ld-2.22.so
7fdb6c071000-7fdb6c1ee000 rw-p 00000000 00:00 0
7fdb6c211000-7fdb6c213000 rw-p 00000000 00:00 0
7fdb6c213000-7fdb6c214000 r--p 00020000 fd:01 264371                     /usr/lib64/ld-2.22.so
7fdb6c214000-7fdb6c215000 rw-p 00021000 fd:01 264371                     /usr/lib64/ld-2.22.so
7fdb6c215000-7fdb6c216000 rw-p 00000000 00:00 0
7ffd1e691000-7ffd1e6b2000 rw-p 00000000 00:00 0                          [stack]
7ffd1e6fc000-7ffd1e6fe000 r--p 00000000 00:00 0                          [vvar]
7ffd1e6fe000-7ffd1e700000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
Aborted
```

PDFium build with address sanitizer :

```
Rendering PDF file min_chrome.pdf.
=================================================================
==1105==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000e9e8 at pc 0x00000059ceca bp 0x7fffffffcdd0 sp 0x7fffffffcdc8

WRITE of size 4 at 0x60200000e9e8 thread T0
    #0 0x59cec9  (/home/test/pdfium_repo/pdfium_test+0x59cec9)
    #1 0x595d87  (/home/test/pdfium_repo/pdfium_test+0x595d87)
    #2 0x5912fc  (/home/test/pdfium_repo/pdfium_test+0x5912fc)
    #3 0x5863c4  (/home/test/pdfium_repo/pdfium_test+0x5863c4)
    #4 0x83e6a0  (/home/test/pdfium_repo/pdfium_test+0x83e6a0)
    #5 0x8403e4  (/home/test/pdfium_repo/pdfium_test+0x8403e4)
    #6 0x7509c3  (/home/test/pdfium_repo/pdfium_test+0x7509c3)
    #7 0x74a3d6  (/home/test/pdfium_repo/pdfium_test+0x74a3d6)
    #8 0x74dc27  (/home/test/pdfium_repo/pdfium_test+0x74dc27)
    #9 0x73bd3c  (/home/test/pdfium_repo/pdfium_test+0x73bd3c)
    #10 0x73b93a  (/home/test/pdfium_repo/pdfium_test+0x73b93a)
    #11 0x757c7b  (/home/test/pdfium_repo/pdfium_test+0x757c7b)
    #12 0x758624  (/home/test/pdfium_repo/pdfium_test+0x758624)
    #13 0x7409fc  (/home/test/pdfium_repo/pdfium_test+0x7409fc)
    #14 0x73d3b1  (/home/test/pdfium_repo/pdfium_test+0x73d3b1)
    #15 0x7306c2  (/home/test/pdfium_repo/pdfium_test+0x7306c2)
    #16 0x7386bb  (/home/test/pdfium_repo/pdfium_test+0x7386bb)
    #17 0x6403d2  (/home/test/pdfium_repo/pdfium_test+0x6403d2)
    #18 0x63fcfa  (/home/test/pdfium_repo/pdfium_test+0x63fcfa)
    #19 0x4e2899  (/home/test/pdfium_repo/pdfium_test+0x4e2899)
    #20 0x4e3c96  (/home/test/pdfium_repo/pdfium_test+0x4e3c96)
    #21 0x4e50c3  (/home/test/pdfium_repo/pdfium_test+0x4e50c3)
    #22 0x7ffff666757f  (/lib64/libc.so.6+0x2057f)

0x60200000e9e8 is located 8 bytes to the left of 1-byte region [0x60200000e9f0,0x60200000e9f1)
allocated by thread T0 here:
    #0 0x4b0fe1  (/home/test/pdfium_repo/pdfium_test+0x4b0fe1)
    #1 0x596f4b  (/home/test/pdfium_repo/pdfium_test+0x596f4b)
    #2 0x5912fc  (/home/test/pdfium_repo/pdfium_test+0x5912fc)
    #3 0x5863c4  (/home/test/pdfium_repo/pdfium_test+0x5863c4)
    #4 0x83e6a0  (/home/test/pdfium_repo/pdfium_test+0x83e6a0)
    #5 0x8403e4  (/home/test/pdfium_repo/pdfium_test+0x8403e4)
    #6 0x7509c3  (/home/test/pdfium_repo/pdfium_test+0x7509c3)
    #7 0x74a3d6  (/home/test/pdfium_repo/pdfium_test+0x74a3d6)
    #8 0x74dc27  (/home/test/pdfium_repo/pdfium_test+0x74dc27)
    #9 0x73bd3c  (/home/test/pdfium_repo/pdfium_test+0x73bd3c)
    #10 0x73b93a  (/home/test/pdfium_repo/pdfium_test+0x73b93a)
    #11 0x757c7b  (/home/test/pdfium_repo/pdfium_test+0x757c7b)
    #12 0x758624  (/home/test/pdfium_repo/pdfium_test+0x758624)
    #13 0x7409fc  (/home/test/pdfium_repo/pdfium_test+0x7409fc)
    #14 0x73d3b1  (/home/test/pdfium_repo/pdfium_test+0x73d3b1)
    #15 0x7306c2  (/home/test/pdfium_repo/pdfium_test+0x7306c2)
    #16 0x7386bb  (/home/test/pdfium_repo/pdfium_test+0x7386bb)
    #17 0x6403d2  (/home/test/pdfium_repo/pdfium_test+0x6403d2)
    #18 0x63fcfa  (/home/test/pdfium_repo/pdfium_test+0x63fcfa)
    #19 0x4e2899  (/home/test/pdfium_repo/pdfium_test+0x4e2899)
    #20 0x4e3c96  (/home/test/pdfium_repo/pdfium_test+0x4e3c96)
    #21 0x4e50c3  (/home/test/pdfium_repo/pdfium_test+0x4e50c3)
    #22 0x7ffff666757f  (/lib64/libc.so.6+0x2057f)

SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/test/pdfium_repo/pdfium_test+0x59cec9)
Shadow bytes around the buggy address:
  0x0c047fff9ce0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9cf0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9d00: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9d10: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff9d20: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
=>0x0c047fff9d30: fa fa fa fa fa fa 01 fa fa fa 01 fa fa[fa]01 fa
  0x0c047fff9d40: fa fa 00 00 fa fa 00 04 fa fa 00 00 fa fa 00 00
  0x0c047fff9d50: fa fa 00 00 fa fa 00 00 fa fa 00 00 fa fa fd fd
  0x0c047fff9d60: fa fa 00 00 fa fa 04 fa fa fa 04 fa fa fa fd fa
  0x0c047fff9d70: fa fa 01 fa fa fa 00 fa fa fa 00 fa fa fa 00 00
  0x0c047fff9d80: fa fa 00 fa fa fa fd fa fa fa fd fd fa fa 04 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
  Heap right redzone:      fb
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack partial redzone:   f4
  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
==1105==ABORTING
   ```

Debugging output of Chromium nightly build revision 392151 (latest at the time of writting):

```
./chrome-wrapper
[12918:12918:0507/003258:ERROR:navigation_entry_screenshot_manager.cc(141)] Invalid entry with unique id: 14
[1:7:0507/003301:ERROR:WaitUntilObserver.cpp(144)] Not implemented reached in void blink::WaitUntilObserver::reportError(const blink::ScriptValue &)
../../third_party/tcmalloc/chromium/src/tcmalloc.cc:289] Attempt to free invalid pointer 0x1df4f0ce0
Received signal 11 SEGV_MAPERR 000000000039
#0 0x55d6de8f0857 base::debug::(anonymous namespace)::StackDumpSignalHandler()
#1 0x7fc1981089f0 <unknown>
#2 0x55d6daced180 <unknown>
#3 0x55d6dacf7954 tcmalloc::Log()
#4 0x55d6dad03070 (anonymous namespace)::do_free_with_callback()
#5 0x55d6de8b2630 opj_jp2_destroy
#6 0x55d6de8b4b29 opj_destroy_codec
#7 0x55d6df08da0a CCodec_JpxModule::CreateDecoder()
#8 0x55d6df045250 CPDF_DIBSource::LoadJpxBitmap()
#9 0x55d6df0436cc CPDF_DIBSource::CreateDecoder()
#10 0x55d6df044616 CPDF_DIBSource::StartLoadDIBSource()
#11 0x55d6df03f5bd CPDF_ImageCacheEntry::StartGetCachedBitmap()
#12 0x55d6df03f46f CPDF_PageRenderCache::StartGetCachedBitmap()
#13 0x55d6df046f04 CPDF_ImageLoaderHandle::Start()
#14 0x55d6df0470e4 CPDF_ImageLoader::Start()
#15 0x55d6df040906 CPDF_ImageRenderer::StartLoadDIBSource()
#16 0x55d6df03fc30 CPDF_ImageRenderer::Start()
#17 0x55d6df03c693 CPDF_RenderStatus::ContinueSingleObject()
#18 0x55d6df03e99b CPDF_ProgressiveRenderer::Continue()
#19 0x55d6defeae44 FPDF_RenderPage_Retail()
#20 0x55d6defe5494 FPDF_RenderPageBitmap_Start
#21 0x55d6de8cfe12 chrome_pdf::PDFiumEngine::ContinuePaint()
#22 0x55d6de8cfaf2 chrome_pdf::PDFiumEngine::Paint()
#23 0x55d6de8e409c chrome_pdf::OutOfProcessInstance::OnPaint()
#24 0x55d6de8e8d7b PaintManager::DoPaint()
#25 0x55d6de81fba4 pp::CompletionCallbackFactory<>::CallbackData<>::Thunk()
#26 0x55d6dc2ac733 ppapi::TrackedCallback::Run()
#27 0x55d6dcb3e0cf ppapi::proxy::PluginResource::OnReplyReceived()
#28 0x55d6dcb3dd9f ppapi::proxy::PluginMessageFilter::DispatchResourceReply()
#29 0x55d6de8f1936 base::debug::TaskAnnotator::RunTask()
#30 0x55d6de907cd5 base::MessageLoop::RunTask()
#31 0x55d6de907fc8 base::MessageLoop::DeferOrRunPendingTask()
#32 0x55d6de9082eb base::MessageLoop::DoWork()
#33 0x55d6de9096ca base::MessagePumpDefault::Run()
#34 0x55d6de92291e base::RunLoop::Run()
#35 0x55d6de9075aa base::MessageLoop::Run()
#36 0x55d6de658fcb content::PpapiPluginMain()
#37 0x55d6de8cb65d content::RunZygote()
#38 0x55d6de8cc6e7 content::ContentMainRunnerImpl::Run()
#39 0x55d6de8cb230 content::ContentMain()
#40 0x55d6da8ceb5b ChromeMain
#41 0x7fc195102580 __libc_start_main
#42 0x55d6da8cea39 <unknown>
  r8: 0000000000000009  r9: cccccccccccccccd r10: 0000000000000004 r11: 0000000000000000
 r12: 000055d6e0b999d0 r13: 00007ffea3ceb8c0 r14: 00007ffea3ceb798 r15: 0000000000000001
  di: 0000000000000000  si: 00007ffea3ceb798  bp: 000055d6df4fee01  bx: 0000000000000066
  dx: 0000000000000066  ax: 0000000000000000  cx: 00007fc198107aed  sp: 00007ffea3ceb768
  ip: 000055d6daced180 efl: 0000000000010297 cgf: 0000000000000033 erf: 0000000000000006
 trp: 000000000000000e msk: 0000000000000000 cr2: 0000000000000039
[end of stack trace]
```

Latest Chrome release crashes upon opening the attached PDF file in a similar manner.

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.

http://talosintel.com/vulnerability-reports/

Timeline

2016-05-19: Bug reported
2016-05-19: Bug acknowledged
2016-05-20: Bug fixed, with fix publicly available in chromium
2016-05-25: Bug fix shipped in Chrome Stable 51.0.2704.63
2016-06-08: Talos releases details