CVE-2017-5133
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.
Google Chrome 60.0.3112.101
https://pdfium.googlesource.com
7.5 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-193: Off-by-one Error
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.
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
2017-09-05 - Vendor Disclosure
2017-10-19 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.