CVE-2016-3319
An exploitable out of bounds write vulnerability exists in the PDF parsing API in the latest versions of Microsoft Windows. A specially crafted PDF file can cause an out of bounds write resulting in arbitrary code execution. Vulnerability can be triggered via malicious web page or a saved PDF file delivered by other means.
Microsoft Windows PDF API Windows.Data.Pdf.dll version 10.0.10.586.162
7.5 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H
The vulnerability is present in the Microsoft native PDF API which is available since Windows 8.1. In Windows 10, Microsoft Edge is the default application for opening PDF files enabling potential vulnerabilities in native PDF API to be exploited over the Web.
There exists a vulnerability in the way Microsoft PDF API parses jpeg2000 files embedded in the PDF documents. A specially crafted jpeg2000 file can trigger a out of bounds memory overwrite and lead to remote code execution.
Jpeg2000 files consist of a number of containers or boxes. Contiguous Codestream box contains the actual image data in the jp2 file and can have a number of child boxes. Contiguous Codestream box starts with a “jp2c” marker. According to the standard, jp2c box can contain SIZ marker segment (image and tile size info), a COD marker segment (coding style default info), a QCD marker segment (quantization default information) and a number of tile part elements which can in turn contain their own COD, CQD and other child elements. Start markers for SIZ is 0xFF51, COD 0xFF52, QCD is 0xFF5C and tile part is 0xFF90. An example of the file layout can be as follows:
```
+------------------+
| |
| Codestream Box |
| |
+-------+----------+
|
| +---------+
+------------+ SIZ |
| +---------+
| +---------+
+------------+ COD |
| +---------+
| +---------+
+------------+ CQD |
| +---------+
| +---------------+
+------------+ Tile Parts |
+------+--------+
|
| +-----------+
+------+ Tile Part |
| +-----+-----+
| | +---------+
| +---+ COD* |
| | +---------+
| | +---------+
| +---+ CQD* |
| | +---------+
| | +---------+
| +---+ COM* |
| | +---------+
| | +---------+
| +---+ SOT |
| +---------+
|
| +-------------+ *Optional
+-------+ Tile Part |
+-------------+
```
According to the standard, tile part elements can contain only COD, CQD, COM, and SOT elements where COD, CQD and COM are optional. In the supplied testcase triggering the vulnerability a tile part element has an unexpected SIZ element which gets parsed and leads to a vulnerability.
Elements are parsed one by one in a CCodeStreamDecoder::DecodeMarkers
method where for each marker type, a suitable decoder is called:
```
.text:6E1EC62D push esi
.text:6E1EC62E mov esi, [ebp+var_74]
.text:6E1EC631 mov ecx, edi ; _DWORD
.text:6E1EC633 push esi
.text:6E1EC634 call ds:___guard_check_icall_fptr ; CType1NoOpReceiver<IType1EncodingReceiver>::Begin(void)
.text:6E1EC63A call edi ; calls the decoder for specific marker
.text:6E1EC63C jmp short loc_6E
```
When parsing a SIZ element, edi
in the above code calls the CCodeStreamDecoder::s_SIZMarkerDecoder
method. In it, various values are initialized. Amongst other things, SIZ marker specifies the number of components (csiz) as an 16 bit integer. This value is used to resize a vectors holding COD and CQD information:
```
.text:6E1EE747 mov eax, [edi]
.text:6E1EE749 mov esi, [eax+10h]
.text:6E1EE74C mov ecx, esi ; _DWORD
.text:6E1EE74E call ds:___guard_check_icall_fptr ; CType1NoOpReceiver<IType1EncodingReceiver>::Begin(void)
.text:6E1EE754 mov ecx, edi
.text:6E1EE756 call esi ; [1]
.text:6E1EE758 movzx esi, ax
.text:6E1EE75B lea ecx, [ebx+78h]
.text:6E1EE75E push esi
.text:6E1EE75F mov [esp+0C4h+var_8C], esi
.text:6E1EE763 mov [ebx+3Ch], esi
.text:6E1EE766 call std::vector<QCD_MARKER,std::allocator<QCD_MARKER>>::resize(uint) [2]
.text:6E1EE76B push esi
.text:6E1EE76C lea ecx, [ebx+84h]
.text:6E1EE772 call std::vector<COD_MARKER,std::allocator<COD_MARKER>>::resize(uint) [3]
```
In the above disassembly, at [1] csiz value is read from the bytestream, at [2], it’s used to resize the QCD_MARKER vector, and at [3] the same for COD_MARKER vector. The pointers to both are stored at ebx+78h
and ebx+84h
respectively.
Next, COD marker decoder is called, CCodeStreamDecoder::s_CODMarkerDecoder
, where the above resized vector is used in it’s elements initialized in a loop:
```
.text:6E1ED9BD mov ecx, [edi+84h] [1]
.text:6E1ED9C3 lea eax, [esp+0A4h+var_88]
.text:6E1ED9C7 push eax
.text:6E1ED9C8 add ecx, esi [2]
.text:6E1ED9CA call COD_MARKER::operator=(COD_MARKER const &) [3]
.text:6E1ED9CF inc ebx
.text:6E1ED9D0 add esi, 48h [4]
.text:6E1ED9D3 cmp ebx, [edi+3Ch] [5]
.text:6E1ED9D6 jb short loc_6E1
```
At [1] a pointer to the vector is retrieved, at [2] esi
is used as an index into the vector values, and is added to ecx
, at [3] the current vector element is used with its assignment operator, at [4] index is increased, and at [5] the counter is compared to the previously mentioned csiz value.
A similar codepath is executed while parsing the CQD marker.
With page heap enabled , the supplied testcase crashes with the following:
```
eax=0d4fe801 ebx=00000003 ecx=0d9a1000 edx=00000000 esi=0d4fe8d0 edi=0d9a1000
eip=6e2cc0cb esp=0d4fe8a0 ebp=0d4fe8a8 iopl=0 nv up ei pl nz ac pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010216
Windows_Data_Pdf!COD_MARKER::operator=+0xe:
6e2cc0cb 8807 mov byte ptr [edi],al ds:002b:0d9a1000=??
0:014> k 5
# ChildEBP RetAddr
00 0d4fe8a8 6e2cd9fa Windows_Data_Pdf!COD_MARKER::operator=+0xe
01 0d4fe95c 6e2cc63c Windows_Data_Pdf!CCodeStreamDecoder::s_CODMarkerDecoder+0x1ea
02 0d4fe9fc 6e2c9127 Windows_Data_Pdf!CCodeStreamDecoder::DecodeMarkers+0x91
03 0d4fec0c 6e2c8b47 Windows_Data_Pdf!JPXDecoder::Decode+0x80
04 0d4fec70 6e2c8740 Windows_Data_Pdf!PDF::CJPXDecoderByteStream::_Decode+0xdf
```
It’s crashing in the COD_MARKER assignment operator with a write access violation. A step by step examination leads to the details of the crash. Firstly, COD_MARKER vector is resized to 3 inside CCodeStreamDecoder::s_SIZMarkerDecoder
method:
```
Breakpoint 2 hit
eax=00000000 ebx=0d3cee0c ecx=0d3cee90 edx=00000000 esi=00000003 edi=0d3cf03c
eip=6e2ce772 esp=0d3cec8c ebp=0d3ced54 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
Windows_Data_Pdf!CCodeStreamDecoder::s_SIZMarkerDecoder+0x172:
6e2ce772 e8e4ebffff call Windows_Data_Pdf!std::vector<COD_MARKER,std::allocator<COD_MARKER> >::resize (6e2cd35b)
0:013> ub
Windows_Data_Pdf!CCodeStreamDecoder::s_SIZMarkerDecoder+0x158:
6e2ce758 0fb7f0 movzx esi,ax
6e2ce75b 8d4b78 lea ecx,[ebx+78h]
6e2ce75e 56 push esi
6e2ce75f 89742438 mov dword ptr [esp+38h],esi
6e2ce763 89733c mov dword ptr [ebx+3Ch],esi
6e2ce766 e884ecffff call Windows_Data_Pdf!std::vector<QCD_MARKER,std::allocator<QCD_MARKER> >::resize (6e2cd3ef)
6e2ce76b 56 push esi
6e2ce76c 8d8b84000000 lea ecx,[ebx+84h]
0:013> dd ecx
0d3cee90 00000000 00000000 00000000 00000000
0d3ceea0 00000000 00000000 00000000 00000000
0d3ceeb0 00000000 0d3ceec8 00000011 00000000
0d3ceec0 0d7e0f1c 6e087f00 6e30c61f 00000064
0d3ceed0 0000000f e16b33b0 0d00ef58 6e30c298
0d3ceee0 6e2c965e 0d7e0f08 6e2c966a e16b3398
0d3ceef0 0d3cef30 6e2c96c2 6e2c96f3 00000000
0d3cef00 0000001c 00000000 00000000 00000000
0:013> p
Breakpoint 2 hit
eax=00000000 ebx=0d3cee0c ecx=0d3cee90 edx=00000000 esi=00000003 edi=0d3cf03c
eip=6e2ce772 esp=0d3cec8c ebp=0d3ced54 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
Windows_Data_Pdf!CCodeStreamDecoder::s_SIZMarkerDecoder+0x172:
6e2ce772 e8e4ebffff call Windows_Data_Pdf!std::vector<COD_MARKER,std::allocator<COD_MARKER> >::resize (6e2cd35b)
0:013> p
eax=000000d8 ebx=0d3cee0c ecx=6e2cd399 edx=00000000 esi=00000003 edi=0d3cf03c
eip=6e2ce777 esp=0d3cec90 ebp=0d3ced54 iopl=0 nv up ei pl nz ac pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216
Windows_Data_Pdf!CCodeStreamDecoder::s_SIZMarkerDecoder+0x177:
6e2ce777 8d4340 lea eax,[ebx+40h]
0:013> dd 0d3cee90
0d3cee90 0d842f28 0d843000 0d843000 00000000
0d3ceea0 00000000 00000000 00000000 00000000
0d3ceeb0 00000000 0d3ceec8 00000011 00000000
0d3ceec0 0d7e0f1c 6e087f00 6e30c61f 00000064
0d3ceed0 0000000f e16b33b0 0d00ef58 6e30c298
0d3ceee0 6e2c965e 0d7e0f08 6e2c966a e16b3398
0d3ceef0 0d3cef30 6e2c96c2 6e2c96f3 00000000
0d3cef00 0000001c 00000000 00000000 00000000
```
In the above debugging output, it can be seen that the resize argument is 3 and that this
is pointing to 0x0d3cee90
. Also, after the call, the location at 0d3cee90
is initialized with pointers to vector start and end, so initially the COD_MARKER vector starts at 0d842f28
. Placing a breakpoint at above mentioned COD vector assignment code inside CCodeStreamDecoder::s_CODMarkerDecoder
gives:
```
Breakpoint 3 hit
eax=0d3cecc8 ebx=00000000 ecx=0d842f28 edx=000000bb esi=00000000 edi=0d3cee0c
eip=6e2cd9ca esp=0d3ceca8 ebp=0d3ced54 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
Windows_Data_Pdf!CCodeStreamDecoder::s_CODMarkerDecoder+0x1ba:
6e2cd9ca e8eee6ffff call Windows_Data_Pdf!COD_MARKER::operator= (6e2cc0bd)
0:013> ub
Windows_Data_Pdf!CCodeStreamDecoder::s_CODMarkerDecoder+0x1a3:
6e2cd9b3 c60701 mov byte ptr [edi],1
6e2cd9b6 395f3c cmp dword ptr [edi+3Ch],ebx
6e2cd9b9 7655 jbe Windows_Data_Pdf!CCodeStreamDecoder::s_CODMarkerDecoder+0x200 (6e2cda10)
6e2cd9bb 8bf3 mov esi,ebx
6e2cd9bd 8b8f84000000 mov ecx,dword ptr [edi+84h]
6e2cd9c3 8d44241c lea eax,[esp+1Ch]
6e2cd9c7 50 push eax
6e2cd9c8 03ce add ecx,esi
0:013> dd ecx
0d842f28 c0c0c0c0 00000000 00000000 00000000
0d842f38 00000000 00000000 00000000 c0c0c0c0
0d842f48 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
0d842f58 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
0d842f68 c0c0c0c0 c0c0c0c0 c0c0c0c0 00000000
0d842f78 00000000 00000000 00000000 00000000
0d842f88 00000000 c0c0c0c0 c0c0c0c0 c0c0c0c0
0d842f98 c0c0c0c0 c0c0c0c0 c0c0c0c0 c0c0c0c0
```
In the above debugging output, we hit a breakpoint on a call to Windows_Data_Pdf!COD_MARKER::operator=
and can see that ecx
points to the beginning of the resized vector from the previous disassembly. It continues to loop 3 times as specified by csiz value.
As mentioned before, the supplied testcase has a tile part that contains an extra SIZ marker along with COD marker, so resuming the execution breaks again in CCodeStreamDecoder::s_SIZMarkerDecoder
during COD_MARKER vector resize:
```
Breakpoint 2 hit
eax=00000003 ebx=0d3cee0c ecx=0d3cee90 edx=00000000 esi=00000004 edi=0d3cf03c
eip=6e2ce772 esp=0d3cec8c ebp=0d3ced54 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
Windows_Data_Pdf!CCodeStreamDecoder::s_SIZMarkerDecoder+0x172:
6e2ce772 e8e4ebffff call Windows_Data_Pdf!std::vector<COD_MARKER,std::allocator<COD_MARKER> >::resize (6e2cd35b)
0:013> bu
breakpoint 2 redefined
0:013> ub
Windows_Data_Pdf!CCodeStreamDecoder::s_SIZMarkerDecoder+0x158:
6e2ce758 0fb7f0 movzx esi,ax
6e2ce75b 8d4b78 lea ecx,[ebx+78h]
6e2ce75e 56 push esi
6e2ce75f 89742438 mov dword ptr [esp+38h],esi
6e2ce763 89733c mov dword ptr [ebx+3Ch],esi
6e2ce766 e884ecffff call Windows_Data_Pdf!std::vector<QCD_MARKER,std::allocator<QCD_MARKER> >::resize (6e2cd3ef)
6e2ce76b 56 push esi
6e2ce76c 8d8b84000000 lea ecx,[ebx+84h]
0:013> dd ecx
0d3cee90 0d842f28 0d843000 0d843000 00000000
0d3ceea0 00000000 00000000 00000000 00000000
0d3ceeb0 00000000 00000008 00000008 00000000
0d3ceec0 0d7e0f1c 6e087f01 00000004 00000000
0d3ceed0 01001002 e16b3348 0d00eea0 00000000
0d3ceee0 000001af 00000146 00000000 00000000
0d3ceef0 00000100 00000100 00000000 00000000
0d3cef00 00000003 0d85cff0 0d85cffc 0d85cffc
0:013> p
eax=00000048 ebx=0d3cee0c ecx=6e2cd399 edx=00000000 esi=00000004 edi=0d3cf03c
eip=6e2ce777 esp=0d3cec90 ebp=0d3ced54 iopl=0 nv up ei pl nz ac pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216
Windows_Data_Pdf!CCodeStreamDecoder::s_SIZMarkerDecoder+0x177:
6e2ce777 8d4340 lea eax,[ebx+40h]
0:013> dd 0d3cee90
0d3cee90 0dab8ee0 0dab9000 0dab9000 00000000
0d3ceea0 00000000 00000000 00000000 00000000
0d3ceeb0 00000000 00000008 00000008 00000000
0d3ceec0 0d7e0f1c 6e087f01 00000004 00000000
0d3ceed0 01001002 e16b3348 0d00eea0 00000000
0d3ceee0 000001af 00000146 00000000 00000000
0d3ceef0 00000100 00000100 00000000 00000000
0d3cef00 00000003 0d85cff0 0d85cffc 0d85cffc
```
In the above debugging output, a call to resize is made with the same this
as previously. Before the call, the same vector pointers are at 0d3cee90
, but after the call, the vector has been reallocated because of resize. In the above code, the value of the esi
, or the argument to resize, is 4 which is the csize value specified in the second SIZ element in the file. In short, the COD_MARKER vector had to be resized to 4 which ended up reallocating it, meaning that previous saved pointers are invalidated. Continuing execution leads us again to COD element decoder, but this time a different path is taken. Instead of new reference to a resized vector, an old one is used but with a new counter:
```
eax=0d3cecc8 ebx=00000000 ecx=0d872f28 edx=00001ec6 esi=00000000 edi=0d3cee0c
eip=6e2cd9f5 esp=0d3ceca8 ebp=0d3ced54 iopl=0 nv up ei pl nz na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000206
Windows_Data_Pdf!CCodeStreamDecoder::s_CODMarkerDecoder+0x1e5:
6e2cd9f5 e8c3e6ffff call Windows_Data_Pdf!COD_MARKER::operator= (6e2cc0bd)
0:013> ub
Windows_Data_Pdf!CCodeStreamDecoder::s_CODMarkerDecoder+0x1d2:
6e2cd9e2 5f pop edi
6e2cd9e3 3c76 cmp al,76h
6e2cd9e5 1d8bf38b8f sbb eax,8F8BF38Bh
6e2cd9ea 3c01 cmp al,1
6e2cd9ec 0000 add byte ptr [eax],al
6e2cd9ee 8d44241c lea eax,[esp+1Ch]
6e2cd9f2 50 push eax
6e2cd9f3 03ce add ecx,esi
0:013> dd ecx
0d872f28 c0c0c001 00000000 00000000 00000000
0d872f38 00000000 00000000 00000000 00000000
0d872f48 00000000 00000005 00000001 00000001
0d872f58 00000020 00000020 00000000 c0c00000
0d872f68 00000001 00000001 c0c0c001 00000000
0d872f78 00000000 00000000 00000000 00000000
0d872f88 00000000 00000000 00000000 00000005
0d872f98 00000001 00000001 00000020 00000020
0:013> u
Windows_Data_Pdf!CCodeStreamDecoder::s_CODMarkerDecoder+0x1e5:
6e2cd9f5 e8c3e6ffff call Windows_Data_Pdf!COD_MARKER::operator= (6e2cc0bd)
6e2cd9fa 43 inc ebx
6e2cd9fb 83c648 add esi,48h
6e2cd9fe 3b5f3c cmp ebx,dword ptr [edi+3Ch]
6e2cda01 72e5 jb Windows_Data_Pdf!CCodeStreamDecoder::s_CODMarkerDecoder+0x1d8 (6e2cd9e8)
6e2cda03 6afe push 0FFFFFFFEh
6e2cda05 58 pop eax
6e2cda06 2b442414 sub eax,dword ptr [esp+14h]
0:013> dd edi+3c L1
0d3cee48 00000004
```
In the above debugging output, we can see that the max counter value is 4 (as specified by new csiz value) but the vector being used is still the same as before, 0d872f28
. In the fourth iteration of this loop, the index into the vector elements will be increased past the allocated heap chunk resulting in an out of bound memory access:
```
Breakpoint 4 hit
eax=0d3cecc8 ebx=00000003 ecx=0d873000 edx=00000000 esi=000000d8 edi=0d3cee0c
eip=6e2cd9f5 esp=0d3ceca8 ebp=0d3ced54 iopl=0 nv up ei pl nz ac pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000216
Windows_Data_Pdf!CCodeStreamDecoder::s_CODMarkerDecoder+0x1e5:
6e2cd9f5 e8c3e6ffff call Windows_Data_Pdf!COD_MARKER::operator= (6e2cc0bd)
0:013> dd ecx
0d873000 ???????? ???????? ???????? ????????
0d873010 ???????? ???????? ???????? ????????
0d873020 ???????? ???????? ???????? ????????
0d873030 ???????? ???????? ???????? ????????
0d873040 ???????? ???????? ???????? ????????
0d873050 ???????? ???????? ???????? ????????
0d873060 ???????? ???????? ???????? ????????
0d873070 ???????? ???????? ???????? ????????
```
This ultimately leads to a crash due to invalid memory write.
With page heap turned off, memory past the end of the heap chunk above will be readable and writable so the process wouldn’t crash there. By carefully controlling the contents of the memory past the adjacent chunk further memory corruption can be achieved possibly leading to arbitrary code execution. Similarly to COD marker, CQD marker parsing is affected with the same out of bounds access/write issue. A stale reference is being reused there too leading to other interesting memory overwrite primitives:
```
.text:6E2CE03F mov ecx, [ebx+130h]
.text:6E2CE045 lea eax, [esp+0ACh+var_8C]
.text:6E2CE049 push eax
.text:6E2CE04A add ecx, edi
.text:6E2CE04C call QCD_MARKER::operator=(QCD_MARKER const &)
.text:6E2CE051 inc esi
.text:6E2CE052 add edi, 20h
.text:6E2CE055 cmp esi, [ebx+3Ch]
.text:6E2CE058 jb short loc_6E2CE0
```
Above disassembly is from CCodeStreamDecoder::s_QCDMarkerDecoder
method call and in it, the stale CQD vector pointer is used to iterate through its elements with an assignment operator. The same out of bounds issue occurs and leads to a different crash inside QCD_MARKER::operator=
where a call to memmove
is passed an pointer from invalid memory.
Finally, without page heap, the following crash occurs:
```
(218.fa0): Windows Runtime Originate Error - code 40080201 (first chance)
(218.fa0): C++ EH exception - code e06d7363 (first chance)
(218.fa0): Windows Runtime Originate Error - code 40080201 (first chance)
(218.fa0): C++ EH exception - code e06d7363 (first chance)
(218.13cc): C++ EH exception - code e06d7363 (first chance)
HEAP[mspdf.exe]: Invalid address specified to RtlFreeHeap( 00A90000, 033B4D40 )
(218.13cc): Break instruction exception - code 80000003 (first chance)
eax=00258000 ebx=033b4d38 ecx=033b4d38 edx=0000003f esi=00a90000 edi=00000000
eip=7778ee8a esp=0426da84 ebp=0426da9c iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
ntdll!RtlpBreakPointHeap+0x19:
7778ee8a cc int 3
0:014> k 10
# ChildEBP RetAddr
00 0426da80 7773cce7 ntdll!RtlpBreakPointHeap+0x19
01 0426da9c 7778e0a8 ntdll!RtlpValidateHeapEntry+0x6c924
02 0426daf4 776ecc3e ntdll!RtlDebugFreeHeap+0xbf
03 0426dbf8 776eb4c8 ntdll!RtlpFreeHeap+0xc3e
04 0426dc24 770577a5 ntdll!RtlFreeHeap+0x268
05 0426dc70 6e029505 msvcrt!free+0x65
06 0426dc80 6e1f9481 Windows_Data_Pdf!std::vector<double,std::allocator<double> >::~vector<double,std::allocator<double> >+0x1a
07 0426dc98 6e1f8e56 Windows_Data_Pdf!std::vector<std::unique_ptr<CTile,std::default_delete<CTile> >,std::allocator<std::unique_ptr<CTile,std::default_delete<CTile> > > >::_Tidy+0x2f
08 0426dca0 77050ea7 Windows_Data_Pdf!CCodeStreamDecoder::~CCodeStreamDecoder+0x1b
09 0426ed2c 6e1f8b47 msvcrt!_NLG_Return
0a 0426ed90 6e1f8740 Windows_Data_Pdf!PDF::CJPXDecoderByteStream::_Decode+0xdf
0b 0426ed98 6e05660f Windows_Data_Pdf!PDF::CJPXDecoderByteStream::DecodeData+0x10
0c 0426eddc 6e28c7e0 Windows_Data_Pdf!Infra::CByteStreamDecorator::Initialize+0x5f
0d 0426ee08 6e0e6a3f Windows_Data_Pdf!PDF::CPDFFactory::CreateByteStreamJPXDecoder+0x50
0e 0426ef8c 6e0550b9 Windows_Data_Pdf!PDF::CStreamObject::_Decompress+0x9196b
0f 0426efb4 6e1b91ed Windows_Data_Pdf!PDF::CStreamObject::DecodeByteStream+0x49
``` The above crash occurs after the out of bound memory write in COD marker decoder corrupts heap metadata and an invalid pointer gets used during the CQD decoder.
In order to successfully exploit this vulnerability, a high control over the heap contents is needed which can possibly be achieved with calculated placement of tile information and other boxes in the jp2 file. It is also possible that the vulnerability can be triggered multiple times while parsing the same file, giving the attacker even greater control over the overwrites.
In summary, the vulnerability is due to the fact that SIZ element present inside tile data element (which seems to violate the standard) resizes the COD and CQD vectors, but a stale pointer gets reused leading to out of bounds write. A detection of malicious files of this nature can be based on a fact that a one or more tile parts have a SIZ element that specifies csiz greater than the initial, global SIZ element.
Vulnerability analysis is done on a custom simple sample application that utilizes PDF API, but the supplied testcase also crashes in Microsoft Edge browser.
With application verifier and page heap enabled, output of “analyze -v”:
(20.90): Windows Runtime Originate Error - code 40080201 (first chance)
(20.90): C++ EH exception - code e06d7363 (first chance)
(20.90): Windows Runtime Originate Error - code 40080201 (first chance)
(20.90): C++ EH exception - code e06d7363 (first chance)
(20.f3c): C++ EH exception - code e06d7363 (first chance)
===========================================================
VERIFIER STOP 0000000F: pid 0x20: corrupted suffix pattern
063C1000 : Heap handle
094EFCF0 : Heap block
000000D8 : Block size
094EFDC8 : corruption address
===========================================================
This verifier stop is not continuable. Process will be terminated
when you use the `go' debugger command.
===========================================================
(20.f3c): Break instruction exception - code 80000003 (first chance)
eax=060af260 ebx=00000000 ecx=060af260 edx=0000000f esi=094efcf0 edi=72f411a0
eip=72f3cbfe esp=0a4ad8dc ebp=0a4ad900 iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00000202
verifier!VerifierStopMessage+0x27e:
72f3cbfe cc int 3
0:014> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
APPLICATION_VERIFIER_HEAPS_CORRUPTED_HEAP_BLOCK_SUFFIX (f)
Corrupted suffix pattern for heap block.
Most typically this happens for buffer overrun errors. Sometimes the application
verifier places non-accessible pages at the end of the allocation and buffer
overruns will cause an access violation and sometimes the heap block is
followed by a magic pattern. If this pattern is changed when the block gets
freed you will get this break. These breaks can be quite difficult to debug
because you do not have the actual moment when corruption happened.
You just have access to the free moment (stop happened here) and the
allocation stack trace (!heap -p -a HEAP_BLOCK_ADDRESS)
Arguments:
Arg1: 063c1000, Heap handle used in the call.
Arg2: 094efcf0, Heap block involved in the operation.
Arg3: 000000d8, Size of the heap block.
Arg4: 094efdc8, Corruption address.
DUMP_CLASS: 2
DUMP_QUALIFIER: 0
FAULTING_IP:
verifier!VerifierStopMessage+27e
72f3cbfe cc int 3
EXCEPTION_RECORD: (.exr -1)
ExceptionAddress: 72f3cbfe (verifier!VerifierStopMessage+0x0000027e)
ExceptionCode: 80000003 (Break instruction exception)
ExceptionFlags: 00000000
NumberParameters: 1
Parameter[0]: 00000000
FAULTING_THREAD: 00000f3c
DEFAULT_BUCKET_ID: STATUS_BREAKPOINT_AVRF
PROCESS_NAME: mspdf.exe
ERROR_CODE: (NTSTATUS) 0x80000003 - {EXCEPTION} Breakpoint A breakpoint has been reached.
EXCEPTION_CODE: (HRESULT) 0x80000003 (2147483651) - One or more arguments are invalid
EXCEPTION_CODE_STR: 80000003
EXCEPTION_PARAMETER1: 00000000
WATSON_BKT_PROCSTAMP: 5706a632
WATSON_BKT_MODULE: verifier.dll
WATSON_BKT_MODSTAMP: 5632d7df
WATSON_BKT_MODOFFSET: cbfe
WATSON_BKT_MODVER: 10.0.10586.0
MODULE_VER_PRODUCT: Microsoft® Windows® Operating System
BUILD_VERSION_STRING: 10.0.10586.162 (th2_release_sec.160223-1728)
MODLIST_WITH_TSCHKSUM_HASH: 6648028320ab5cbba7b6c72455d4e5c1de630a24
MODLIST_SHA1_HASH: 5c045408b1c06db5d658dde68dc1f6871a92acac
NTGLOBALFLAG: 2000100
APPLICATION_VERIFIER_FLAGS: 48004
PRODUCT_TYPE: 1
SUITE_MASK: 272
APPLICATION_VERIFIER_LOADED: 1
APP: mspdf.exe
ANALYSIS_SESSION_HOST: DESKTOP-G0NTBS7
ANALYSIS_SESSION_TIME: 04-24-2016 20:00:13.0430
ANALYSIS_VERSION: 10.0.10586.567 x86fre
THREAD_ATTRIBUTES:
OS_LOCALE: ENU
PROBLEM_CLASSES:
Tid [0x0]
Frame [0x00]
String [STATUS_BREAKPOINT]
Data Bucketing
AVRF
Tid [0xf3c]
Frame [0x00]: verifier!VerifierStopMessage
Failure Bucketing
BUGCHECK_STR: STATUS_BREAKPOINT_AVRF
STACK_TEXT:
0a4ad900 72f3aa52 0000000f 72f31b80 063c1000 verifier!VerifierStopMessage+0x27e
0a4ad964 72f3ae8a 063c1000 00000000 094efcf0 verifier!AVrfpDphReportCorruptedBlock+0x1c2
0a4ad9c0 72f3bc3b 063c1000 094efcf0 00000000 verifier!AVrfpDphCheckNormalHeapBlock+0x11a
0a4ad9e0 72f411b2 063c0000 094efcf0 0a4ada50 verifier!AvrfpDphCheckPageHeapAllocation+0x6b
0a4ad9f0 72f51def 063c0000 094efcf0 638dce2d verifier!VerifierCheckPageHeapAllocation+0x12
0a4ada50 770577a5 063c0000 00000000 094efcf0 verifier!AVrfpRtlFreeHeap+0x5f
0a4ada9c 72f52dd5 094efcf0 638dcea9 094efdc8 msvcrt!free+0x65
0a4adad4 6e2c934e 094efcf0 e06d7363 19930522 verifier!AVrfp_delete+0x45
0a4adae8 6e2c8f8b 0a4ae96c 6e2c8e61 00000000 Windows_Data_Pdf!std::vector<COD_MARKER,std::allocator<COD_MARKER> >::_Tidy+0x38
0a4adaf0 6e2c8e61 00000000 77050ea7 e06d7363 Windows_Data_Pdf!JPXMetadata::~JPXMetadata+0x26
0a4adaf8 77050ea7 e06d7363 00000000 0a4adb18 Windows_Data_Pdf!CCodeStreamDecoder::~CCodeStreamDecoder+0x26
0a4aeb64 6e2c8b47 094e9de4 00000800 aaa50673 msvcrt!_NLG_Return
0a4aebc8 6e2c8740 094e9dcc 6e12660f 094e9dcc Windows_Data_Pdf!PDF::CJPXDecoderByteStream::_Decode+0xdf
0a4aebd0 6e12660f 094e9dcc 6e1265b0 0a4aec34 Windows_Data_Pdf!PDF::CJPXDecoderByteStream::DecodeData+0x10
0a4aec14 6e35c7e0 aaa5014b 09456e34 6e35c790 Windows_Data_Pdf!Infra::CByteStreamDecorator::Initialize+0x5f
0a4aec40 6e1b6a3f 0a4aec6c 0a4aee4c 094e9d40 Windows_Data_Pdf!PDF::CPDFFactory::CreateByteStreamJPXDecoder+0x50
0a4aedc4 6e1250b9 0a4aee4c 094e9d40 6e125070 Windows_Data_Pdf!PDF::CStreamObject::_Decompress+0x9196b
0a4aedec 6e2891ed 0a4aee4c 094e9d40 aaa51d87 Windows_Data_Pdf!PDF::CStreamObject::DecodeByteStream+0x49
0a4af08c 6e2874e6 0a4af2d8 094e88f8 094e8900 Windows_Data_Pdf!Builder::CImageHandler::_LoadImageObject+0x198
0a4af0d4 6e25a50c 0a4af2d8 094e88f8 094e8900 Windows_Data_Pdf!Builder::CImageHandler::LoadImageObject+0x41
0a4af158 6e353406 0a4af2d8 0a4af3b8 094cb464 Windows_Data_Pdf!Builder::CResourceFactory::LoadImageSource+0xbc
0a4af5f4 6e3101ba 094e943c 6e310190 094e9430 Windows_Data_Pdf!PageElements::GraphicsCommandUpdater::UpdateGraphicsCommand+0xee6
0a4af60c 6e1282da 6e12b880 094e93d0 aaa51b2b Windows_Data_Pdf!std::_Func_impl<std::_Callable_obj<<lambda_267d1e4465265120ffca50182e13906e>,0>,std::allocator<std::_Func_class<void,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> >,void,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::_Do_call+0x2a
0a4af620 6e12b888 6e127fbf 094e91f8 094e9208 Windows_Data_Pdf!std::_Func_class<void,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::operator()+0x5a
0a4af624 6e127fbf 094e91f8 094e9208 aaa51b33 Windows_Data_Pdf!std::_Func_impl<std::_Callable_obj<Infra::GlobalTask,0>,std::allocator<std::_Func_class<bool,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil> >,bool,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::_Do_call+0x8
0a4af638 6e127d2b 6e1dc410 094e9740 00000001 Windows_Data_Pdf!std::_Func_class<bool,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil,std::_Nil>::operator()+0x49
0a4af648 6e1dc42f aaa51b73 6e1dc410 094e9850 Windows_Data_Pdf!Infra::CTask::Execute+0x33
0a4af678 6d8353df 094e9740 094e9850 0a4af894 Windows_Data_Pdf!Infra::CAsyncTpWorker::CTpWorkItem::Invoke+0x1f
0a4af6a4 6d834d20 5ab46f05 0a4af894 08b03ff0 threadpoolwinrt!Windows::System::Threading::CThreadPoolWorkItem::CommonWorkCallback+0xaf
0a4af6d4 776dde13 0a4af894 094e9850 08b03ff0 threadpoolwinrt!Windows::System::Threading::CThreadPoolWorkItem::BatchedCallback+0x60
0a4af7c4 776dcc25 0a4af894 08b04060 738bf041 ntdll!TppWorkpExecuteCallback+0x153
0a4af974 750238f4 08abe0a8 750238d0 f814e18b ntdll!TppWorkerThread+0x555
0a4af988 77715de3 08abe0a8 738bf0e5 00000000 KERNEL32!BaseThreadInitThunk+0x24
0a4af9d0 77715dae ffffffff 7773b7db 00000000 ntdll!__RtlUserThreadStart+0x2f
0a4af9e0 00000000 776dc6d0 08abe0a8 00000000 ntdll!_RtlUserThreadStart+0x1b
THREAD_SHA1_HASH_MOD_FUNC: 6315750fc53d807b4155ffc0842ee6a03c9f40f3
THREAD_SHA1_HASH_MOD_FUNC_OFFSET: f4a0d5cef213b9229c67ca969977acae24171ac9
THREAD_SHA1_HASH_MOD: 7f0f6cd60042c923021fe565fe770b7a413be340
FOLLOWUP_IP:
verifier!VerifierStopMessage+27e
72f3cbfe cc int 3
FAULT_INSTR_CODE: f87d83cc
SYMBOL_STACK_INDEX: 0
SYMBOL_NAME: verifier!VerifierStopMessage+27e
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: verifier
IMAGE_NAME: verifier.dll
DEBUG_FLR_IMAGE_TIMESTAMP: 5632d7df
STACK_COMMAND: ~14s ; kb
BUCKET_ID: STATUS_BREAKPOINT_AVRF_verifier!VerifierStopMessage+27e
PRIMARY_PROBLEM_CLASS: STATUS_BREAKPOINT_AVRF_verifier!VerifierStopMessage+27e
BUCKET_ID_OFFSET: 27e
BUCKET_ID_MODULE_STR: verifier
BUCKET_ID_MODTIMEDATESTAMP: 5632d7df
BUCKET_ID_MODCHECKSUM: 5c097
BUCKET_ID_MODVER_STR: 10.0.10586.0
BUCKET_ID_PREFIX_STR: STATUS_BREAKPOINT_AVRF_
FAILURE_PROBLEM_CLASS: STATUS_BREAKPOINT_AVRF
FAILURE_EXCEPTION_CODE: 80000003
FAILURE_IMAGE_NAME: verifier.dll
FAILURE_FUNCTION_NAME: VerifierStopMessage
BUCKET_ID_FUNCTION_STR: VerifierStopMessage
FAILURE_SYMBOL_NAME: verifier.dll!VerifierStopMessage
FAILURE_BUCKET_ID: STATUS_BREAKPOINT_AVRF_80000003_verifier.dll!VerifierStopMessage
TARGET_TIME: 2016-04-24T18:00:19.000Z
OSBUILD: 10586
OSSERVICEPACK: 0
SERVICEPACK_NUMBER: 0
OS_REVISION: 0
OSPLATFORM_TYPE: x86
OSNAME: Windows 10
OSEDITION: Windows 10 WinNt SingleUserTS
USER_LCID: 0
OSBUILD_TIMESTAMP: 2015-10-30 03:46:21
BUILDDATESTAMP_STR: 160223-1728
BUILDLAB_STR: th2_release_sec
BUILDOSVER_STR: 10.0.10586.162
ANALYSIS_SESSION_ELAPSED_TIME: 176f
ANALYSIS_SOURCE: UM
FAILURE_ID_HASH_STRING: um:status_breakpoint_avrf_80000003_verifier.dll!verifierstopmessage
FAILURE_ID_HASH: {bedb7089-3b9b-ca23-9c37-a0231a6648d3}
Followup: MachineOwner
---------
2016-04-28 - Vendor Disclosure
2016-08-09 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.