Talos Vulnerability Report

TALOS-2017-0432

Google PDFium TIFF Image Flate Decoder Code Execution Vulnerability

October 19, 2017
CVE Number

CVE-2017-5133

Summary

An off-by-one read/write on the heap vulnerability exists in the TIFF image decoder functionality of Pdfium as used by Google Chrome up to and including 60.0.3112.101. A specially crafted PDF file can trigger an off-by-one read and write on the heap resulting in memory corruption and a possible information leak and potential code execution. The victim needs to open a malicious PDF in the browser in order to trigger this vulnerability.

Tested Versions

Google Chrome 60.0.3112.101

Product URLs

https://pdfium.googlesource.com

CVSSv3 Score

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

CWE

CWE-193: Off-by-one Error

Details

Pdfium is an open source PDF renderer developed by Google and used extensively in the Chrome browser, online services as well as other standalone applications. This bug was triaged on the latest git version as well as the latest chromium address sanitizer build available (asan-linux-release-498039).

A heap-buffer overflow is present in the code responsible for decoding a compressed TIFF image stream. While parsing pixel data of the flate decoded image stream, the function TIFF_PredictLine is reached:

void TIFF\_PredictLine(uint8_t* dest_buf,
                   uint32_t row_size,
                   int BitsPerComponent,
                   int Colors,
                   int Columns) {
  

int BytesPerPixel = BitsPerComponent * Colors / 8;
 if (BitsPerComponent == 16) {
 for (uint32_t i = BytesPerPixel; i < row_size; i += 2) {
   uint16_t pixel =
       (dest_buf[i - BytesPerPixel] << 8) | dest_buf[i - BytesPerPixel + 1];
   pixel += (dest_buf[i] << 8) | dest_buf[i + 1];
   dest_buf[i] = pixel >> 8;
   dest_buf[i + 1] = (uint8_t)pixel;

In the above code, during the for loop, 4 bytes will always be read from dest_buffer even if the length of the buffer is less than that. This can potentially lead to an off-by-one read on the heap, followed immediately by an off-by-one write. In order to reach the buggy code and trigger the vulnerable state, a couple of conditions need to be satisfied. In the previous function , TIFF_Predictor, we see:

bool TIFF_Predictor(uint8_t*& data_buf,
                  uint32_t& data_size,
                  int Colors,
                  int BitsPerComponent,
                  int Columns) {
int row_size = (Colors * BitsPerComponent * Columns + 7) / 8;                         [1]
if (row_size == 0)
  return false;
const int row_count = (data_size + row_size - 1) / row_size;
const int last_row_size = data_size % row_size;                                        [2]
for (int row = 0; row < row_count; row++) {
  uint8_t* scan_line = data_buf + row * row_size;
  if ((row + 1) * row_size > (int)data_size) {
    row_size = last_row_size;                                                        [3]
  }
  TIFF_PredictLine(scan_line, row_size, BitsPerComponent, Colors, Columns);        [4]
}
return true;

At [1], row_size is calculated and is a multiple of 8. At [2], the data size of the last row is calculated, as input data might not have a multiple of row_size bytes available. When the last row is being used (if the next row would end up outside the data size) row_size is set to last_row_size at [3]. At [4], vulnerable function TIFF_PredictLine is called with the calculated row_size. If we line up the buffer sizes and last_row_size properly, this can result in last_row_size being 3, where Tiff_PredictLine actually reads/writes 4 bytes from the data buffer , leading to off-by-one read/write.

A sample PDF to trigger this bug is:

%PDF-1.6
                 
47 0 obj
<</DecodeParms
      <<        /Columns 2
      /Colors 1
              /BitsPerComponent 16
              /Predictor 2>>
      /Filter/FlateDecode
      /W[0 0 0]>>
stream
...
endstream
endobj
startxref 30
%%EOF

The content of the stream above just needs to satisfy one condition and that is that it must decode to a length that would result in 3 in the calculation at [2] in the previously mentioned code. The lowest length that satisfies these and some of the previously mentioned conditions is 23. The values of Columns, Colors and uncompressed stream lengths can be adjusted to control the sizes of the buffers, number of loops and bytes accessed and all ultimately get passed to their corresponding values to the functions that we mentioned.

Depending on the underlying allocator and other variables, abusing this bug for information leaks or memory overwrite might or might not be possible, but it could potentially be combined with other vulnerabilities to cause further memory corruption.

Crash Information

Address Sanitizer output from latest build at the time (asan-linux-release-498039)

Rendering PDF file poc_test.pdf.
=================================================================
==67198==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x603000003177 at pc 0x0000025b0826 bp    
0x7fffffffcf70 sp 0x7fffffffcf68
READ of size 1 at 0x603000003177 thread T0
  #0 0x25b0825 in _ZN12_GLOBAL__N_116TIFF_PredictLineEPhjiii 
./out/Release/../../third_party/pdfium/core/fxcodec/codec/fx_codec_flate.cpp:478
  #1 0x25b0825 in ?? ??:0
  #2 0x25b2646 in TIFF_Predictor ./out/Release/../../third_party/pdfium/core/fxcodec/codec/fx_codec_flate.cpp:504
  #3 0x25b2646 in FlateOrLZWDecode ./out/Release/../../third_party/pdfium/core/fxcodec/codec/fx_codec_flate.cpp:805
  #4 0x25b2646 in ?? ??:0
  #5 0x2423440 in _Z24FPDFAPI_FlateOrLZWDecodebPKhjP15CPDF_DictionaryjPPhPj 
./out/Release/../../third_party/pdfium/core/fpdfapi/parser/fpdf_parser_decode.cpp:319
  #6 0x2423440 in ?? ??:0
  #7 0x24240a9 in _Z14PDF_DataDecodePKhjPK15CPDF_DictionaryjbPPhPjP14CFX_ByteStringPPS1_ crtstuff.c:?
  #8 0x24240a9 in ?? ??:0
  #9 0x2412602 in _ZN14CPDF_StreamAcc11LoadAllDataEbjb        
./out/Release/../../third_party/pdfium/core/fpdfapi/parser/cpdf_stream_acc.cpp:45
  #10 0x2412602 in ?? ??:0
  #11 0x23faa1b in _ZN11CPDF_Parser14LoadCrossRefV5EPlb   
./out/Release/../../third_party/pdfium/core/fpdfapi/parser/cpdf_parser.cpp:1085
  #12 0x23faa1b in ?? ??:0
  #13 0x23ed71a in _ZN11CPDF_Parser17LoadAllCrossRefV5El 
./out/Release/../../third_party/pdfium/core/fpdfapi/parser/cpdf_parser.cpp:645
  #14 0x23ed71a in ?? ??:0
  #15 0x23eaf90 in _ZN11CPDF_Parser18StartParseInternalEP13CPDF_Document 
./out/Release/../../third_party/pdfium/core/fpdfapi/parser/cpdf_parser.cpp:248
  #16 0x23eaf90 in ?? ??:0
  #17 0x20f747b in _ZN12_GLOBAL__N_116LoadDocumentImplERK13CFX_RetainPtrI22IFX_SeekableReadStreamEPKc 
./out/Release/../../third_party/pdfium/fpdfsdk/fpdfview.cpp:288
  #18 0x20f747b in ?? ??:0
  #19 0x20f7734 in FPDF_LoadCustomDocument ./out/Release/../../third_party/pdfium/fpdfsdk/fpdfview.cpp:629
  #20 0x20f7734 in ?? ??:0
  #21 0x4f64b0 in
  ZB12_GLOBAL__N_19RenderPdfERKNSt3__112basic_stringIcNS0_11char_traitslcEENS0_9allocatorlcEEEEPKcmRKNS_7Options
  ES8_ ./out/Release/../../third_party/pdfium/samples/pdfium_test.cc:1406
  #22 0x4f64b0 in ?? ??:0
  #23 0x4f3b7f in main ./out/Release/../../third_party/pdfium/samples/pdfium_test.cc:1624
  #24 0x4f3b7f in ?? ??:0
  #25 0x7ffff624e82f in __libc_start_main /build/glibc-bfm8X4/glibc-2.23/csu/../csu/libc-start.c:291
  #26 0x7ffff624e82f in ?? ??:0


  0x603000003177 is located 0 bytes to the right of 23-byte region [0x603000003160,0x603000003177)
  allocated by thread T0 here:
  #0 0x4c48e3 in __interceptor_malloc ??:?
  #1 0x4c48e3 in ?? ??:0
  #2 0x25b2106 in PartitionAllocGenericFlags 

./out/Release/../../third_party/pdfium/third_party/base/allocator/partition_allocator/partition_alloc.h:787
  #3 0x25b2106 in FX_SafeAlloc ./out/Release/../../third_party/pdfium/core/fxcrt/fx_memory.h:46
  #4 0x25b2106 in FX_AllocOrDie ./out/Release/../../third_party/pdfium/core/fxcrt/fx_memory.h:67
  #5 0x25b2106 in FlateUncompress ./out/Release/../../third_party/pdfium/core/fxcodec/codec/fx_codec_flate.cpp:556
  #6 0x25b2106 in FlateOrLZWDecode ./out/Release/../../third_party/pdfium/core/fxcodec/codec/fx_codec_flate.cpp:794
  #7 0x25b2106 in ?? ??:0
  #8 0x2423440 in _Z24FPDFAPI_FlateOrLZWDecodebPKhjP15CPDF_DictionaryjPPhPj     
./out/Release/../../third_party/pdfium/core/fpdfapi/parser/fpdf_parser_decode.cpp:319
  #9 0x2423440 in ?? ??:0
  #10 0x24240a9 in _Z14PDF_DataDecodePKhjPK15CPDF_DictionaryjbPPhPjP14CFX_ByteStringPPS1_ crtstuff.c:?
  #11 0x24240a9 in ?? ??:0
  #12 0x2412602 in _ZN14CPDF_StreamAcc11LoadAllDataEbjb 
./out/Release/../../third_party/pdfium/core/fpdfapi/parser/cpdf_stream_acc.cpp:45
  #13 0x2412602 in ?? ??:0
  #14 0x23faa1b in _ZN11CPDF_Parser14LoadCrossRefV5EPlb 
./out/Release/../../third_party/pdfium/core/fpdfapi/parser/cpdf_parser.cpp:1085
  #15 0x23faa1b in ?? ??:0
  #16 0x23ed71a in _ZN11CPDF_Parser17LoadAllCrossRefV5El 
./out/Release/../../third_party/pdfium/core/fpdfapi/parser/cpdf_parser.cpp:645
  #17 0x23ed71a in ?? ??:0
  #18 0x23eaf90 in _ZN11CPDF_Parser18StartParseInternalEP13CPDF_Document 
./out/Release/../../third_party/pdfium/core/fpdfapi/parser/cpdf_parser.cpp:248
  #19 0x23eaf90 in ?? ??:0
  #20 0x20f747b in _ZN12_GLOBAL__N_116LoadDocumentImplERK13CFX_RetainPtrI22IFX_SeekableReadStreamEPKc 
./out/Release/../../third_party/pdfium/fpdfsdk/fpdfview.cpp:288
  #21 0x20f747b in ?? ??:0
  #22 0x20f7734 in FPDF_LoadCustomDocument ./out/Release/../../third_party/pdfium/fpdfsdk/fpdfview.cpp:629
  #23 0x20f7734 in ?? ??:0
  #24 0x4f64b0 in
 ZN12_GLOBAL_N_19RenderPdfERKNSt3__112basic_stringIcNS0_11char_traitsIcEENS0_9allocatorIcEEEEPKcmRKNS_7OptionsE   
 S8_ ./out/Release/../../third_party/pdfium/samples/pdfium_test.cc:1406
  #25 0x4f64b0 in ?? ??:0
  #26 0x4f3b7f in main ./out/Release/../../third_party/pdfium/samples/pdfium_test.cc:1624
  #27 0x4f3b7f in ?? ??:0
  #28 0x7ffff624e82f in __libc_start_main /build/glibc-bfm8X4/glibc-2.23/csu/../csu/libc-start.c:291
  #29 0x7ffff624e82f in ?? ??:0


SUMMARY: AddressSanitizer: heap-buffer-overflow (/home/user/pdfium/repo/asan-linux-release-  
498039/pdfium_test+0x25b0825)
Shadow bytes around the buggy address:
0x0c067fff85d0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff85e0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff85f0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8600: fa fa fd fd fd fd fa fa fd fd fd fd fa fa fd fd
0x0c067fff8610: fd fd fa fa fd fd fd fa fa fa fd fd fd fa fa fa
=>0x0c067fff8620: fd fd fd fa fa fa fd fd fd fa fa fa 00 00[07]fa
0x0c067fff8630: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8640: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8650: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8660: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c067fff8670: 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
==67198==ABORTING

Official, latest version of Chrome on Windows crashes with the following when run with PageHeap enabled (output from BugId):

BugId:            OOBW[0x1FB]+0~1#b6d7 c40.313 
Location:         chrome.exe!verifier.dll!AVrfpDphCheckPageHeapBlock 
Description:      Page heap detected heap corruption at 0x8EA7FFB; at the end of a 507/0x1FB bytes heap block at 0x8EA7E00. This appears to be a classic 
buffer-overrun vulnerability. The following byte values were written to the corrupted area: 22. 
Version:          chrome.exe: 60.0.3112.113 (x86)
verifier.dll: 6.1.7600.16385 (x86) 
Security impact:  Potentially highly exploitable security issue. 
Integrity level:  0x2000 (Medium Integrity; this process appears to not be sandboxed!) 
Arguments:  ['--enable-experimental-accessibility-features', '--enable-experimental-canvas-features', '--enable-experimental-input-
view-features', '--   
enable-experimental-web-platform-features', '--enable-logging=stderr', '--enable-usermedia-screen-capturing', '--enable-viewport', '--
enable-webgl-draft-
extensions', '--enable-webvr', '--expose-internals-for-testing', '--disable-popup-blocking', '--disable-prompt-on-repost', '--force-
renderer-  
accessibility', 
'--javascript-harmony', '--js-flags="--expose-gc"', '--no-sandbox', 'c:\\Users\\ea\\Desktop\\poc.pdf'] 

Stack: verifier.dll!VerifierStopMessage + 0x1F8 (this frame is irrelevant to this bug) 2.verifier.dll!AVrfpDphReportCorruptedBlock + 0x1C2 (this frame is irrelevant to this bug) 3.verifier.dll!AVrfpDphCheckPageHeapBlock + 0x161 (id: c40) 4.verifier.dll!AVrfpDphFindBusyMemory + 0xDA (id: 313) 5.verifier.dll!AVrfpDphFindBusyMemoryAndRemoveFromBusyList + 0x20 6.ntdll.dll!RtlpDebugPageHeapFree + ? (the exact offset is not known) 7.ntdll.dll!RtlDebugFreeHeap + 0x2F 8.ntdll.dll!RtlpFreeHeap + 0x5D 9.ntdll.dll!RtlFreeHeap + 0x142 10.kernel32.dll!HeapFree + 0x14 11.chrome_child.dll + 0x163239 (no function symbol available) 12.chrome_child.dll + 0x1852FAA (no function symbol available) 13.chrome_child.dll + 0x184DDD1 (no function symbol available) 14.chrome_child.dll + 0x1846493 (no function symbol available) 15.chrome_child.dll + 0x18488BD (no function symbol available) 16.chrome_child.dll + 0x18485B5 (no function symbol available) 17.chrome_child.dll + 0x1823308 (no function symbol available) 18.chrome_child.dll + 0x18175AB (no function symbol available) 19.chrome_child.dll + 0x181413D (no function symbol available) 20.chrome_child.dll + 0x181468D (no function symbol available) 21.chrome_child.dll + 0x181F15A (no function symbol available) 22.chrome_child.dll + 0x181E1AF (no function symbol available) 23.chrome_child.dll + 0x17CA70A (no function symbol available) 24.chrome_child.dll + 0x1437254 (no function symbol available) 25.chrome_child.dll + 0x143797E (no function symbol available) 26.chrome_child.dll + 0x16729C1 (no function symbol available)

Page heap output for heap block near 0x8EA7FFB
 address 08ea7e00 found in
 _DPH_HEAP_ROOT @ 4161000
 in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                              8712000:          8ea7e00              1fb -          8ea7000             2000
 6ccf8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
 77876206 ntdll!RtlDebugAllocateHeap+0x00000030
 7783a127 ntdll!RtlpAllocateHeap+0x000000c4
 77805950 ntdll!RtlAllocateHeap+0x0000023a
 58de52c3 chrome_child!ovly_debug_event+0x0014dff3
 5a357dd2 chrome_child!IsSandboxedProcess+0x003fb31f
 5a3cf306 chrome_child!IsSandboxedProcess+0x00472853
 5a379bca chrome_child!IsSandboxedProcess+0x0041d117
 5a37a05d chrome_child!IsSandboxedProcess+0x0041d5aa
 5a38311c chrome_child!IsSandboxedProcess+0x00426669
 5a376b0e chrome_child!IsSandboxedProcess+0x0041a05b
 5a376493 chrome_child!IsSandboxedProcess+0x004199e0
 5a3788bd chrome_child!IsSandboxedProcess+0x0041be0a
 5a3785b5 chrome_child!IsSandboxedProcess+0x0041bb02
 5a353308 chrome_child!IsSandboxedProcess+0x003f6855
 5a3475ab chrome_child!IsSandboxedProcess+0x003eaaf8
 5a34413d chrome_child!IsSandboxedProcess+0x003e768a
 5a34468d chrome_child!IsSandboxedProcess+0x003e7bda
 5a34f15a chrome_child!IsSandboxedProcess+0x003f26a7
 5a34e1af chrome_child!IsSandboxedProcess+0x003f16fc
 5a2fa70a chrome_child!IsSandboxedProcess+0x0039dc57
 59f67254 chrome_child!IsSandboxedProcess+0x0000a7a1
 59f6797e chrome_child!IsSandboxedProcess+0x0000aecb
 5a1a29c1 chrome_child!IsSandboxedProcess+0x00245f0e
 5a1a2be2 chrome_child!IsSandboxedProcess+0x0024612f
 5a17e7ea chrome_child!IsSandboxedProcess+0x00221d37
 5a17e9fb chrome_child!IsSandboxedProcess+0x00221f48
 58c33f7e chrome_child+0x00103f7e
 58c31129 chrome_child+0x00101129
 58c33bc4 chrome_child+0x00103bc4
 58c996e8 chrome_child!ovly_debug_event+0x00002418
 58f54b95 chrome_child!ChromeMain+0x0000b501

Timeline

2017-09-05 - Vendor Disclosure
2017-10-19 - Public Release

Credit

Discovered by Aleksandar Nikolic of Cisco Talos.