Talos Vulnerability Report

TALOS-2017-0290

AntennaHouse DMC HTMLFilter PPT ParseEnvironment Code Execution Vulnerability

May 4, 2017
CVE Number

CVE-2017-2797

Summary

An exploitable heap overflow vulnerability exists in the ParseEnvironment functionality of AntennaHouse DMC HTMLFilter as used by MarkLogic 8.0-6.  A specially crafted PPT file can cause a heap corruption resulting in arbitrary code execution.  An attacker can send/provide malicious PPT file to trigger this vulnerability.

Tested Versions

AntennaHouse DMC HTMLFilter shipped with MarkLogic 8.0-6
fb1a22fa08c986ec3614284f4e912b0a /opt/MarkLogic/Converters/cvtofc/libdhf_rdoc.so
15b0acc464fba28335239f722a62037f /opt/MarkLogic/Converters/cvtofc/libdmc_comm.so
1eabb31236c675f9856a7d001b339334 /opt/MarkLogic/Converters/cvtofc/libdhf_rxls.so
1415cbc784f05db0e9db424636df581a /opt/MarkLogic/Converters/cvtofc/libdhf_comm.so
4ae366fbd4540dd4c750e6679eb63dd4 /opt/MarkLogic/Converters/cvtofc/libdmc_conf.so
81db1b55e18a0cb70a78410147f50b9c /opt/MarkLogic/Converters/cvtofc/libdhf_htmlif.so
d716dd77c8e9ee88df435e74fad687e6 /opt/MarkLogic/Converters/cvtofc/libdhf_whtml.so
e01d37392e2b2cea757a52ddb7873515 /opt/MarkLogic/Converters/cvtofc/convert

Product URLs

https://www.antennahouse.com/antenna1/

CVSSv3 Score

8.3 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H CVSSv3 Calculator: https://www.first.org/cvss/calculator/3.0

Details

This vulnerability is present in the AntennaHouse DMC HTMLFilter which is used, among others, to convert PPT files to (X)HTML form.   This product is mainly used by MarkLogic for office document conversions as part of their web based document search and rendering engine.  A specially crafted PPT file can lead to heap corruption and ultimately to remote code execution. 

Running the PPT to HTML converter under Valgrind we can see the following result:

icewall@ubuntu:~/bugs/cvtofc_86$ valgrind ./convert config_ppt/
==64384== Memcheck, a memory error detector
==64384== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==64384== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==64384== Command: ./convert config_ppt/
==64384== 
input=/home/icewall/bugs/cvtofc_86/config_ppt/toconv.ppt
output=/home/icewall/bugs/cvtofc_86/config_ppt/conv.html
type=3
info.options='0'
Return from GetFileInfo=0
HtmlInfo.GroupName=UTF-8
HtmlInfo.DefLangName=English
HtmlInfo.bBigEndian=0
HtmlInfo.options=0
HtmlInfo.SheetId=0
HtmlInfo.SlideId=0
HtmlInfo.lpFunc=(nil)
HtmlInfo.szImageFolder=
==64384== Source and destination overlap in strcpy(0x43e178d, 0x43e178d)
==64384==    at 0x402D56F: strcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==64384==    by 0x635186F: DHF_WOpen (in /home/icewall/bugs/cvtofc_86/libdhf_whtml.so)
==64384==    by 0x4039779: FilterToHtml (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)
==64384==    by 0x4038AFB: DHF_GetHtml_V11 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)
==64384==    by 0x8049AF7: main (in /home/icewall/bugs/cvtofc_86/convert)
==64384== 
==64384== Invalid write of size 4
==64384==    at 0x402EE82: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==64384==    by 0x42DF3D2: DMC_2OLEreadStream (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)
==64384==    by 0x4048003: ParseEnvironment (in /home/icewall/bugs/cvtofc_86/libdhf_rppt.so)
==64384==    by 0x4043F98: ParseDocument (in /home/icewall/bugs/cvtofc_86/libdhf_rppt.so)
==64384==    by 0x4043C6D: InitDocument (in /home/icewall/bugs/cvtofc_86/libdhf_rppt.so)
==64384==    by 0x40424E1: DHF_RGetObject (in /home/icewall/bugs/cvtofc_86/libdhf_rppt.so)
==64384==    by 0x403979E: FilterToHtml (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)
==64384==    by 0x4038AFB: DHF_GetHtml_V11 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)
==64384==    by 0x8049AF7: main (in /home/icewall/bugs/cvtofc_86/convert)
==64384==  Address 0x4c0f1a4 is 0 bytes after a block of size 76 alloc'd
==64384==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==64384==    by 0x42DCCB1: DMC_malloc (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)
==64384==    by 0x4047FF2: ParseEnvironment (in /home/icewall/bugs/cvtofc_86/libdhf_rppt.so)
==64384==    by 0x4043F98: ParseDocument (in /home/icewall/bugs/cvtofc_86/libdhf_rppt.so)
==64384==    by 0x4043C6D: InitDocument (in /home/icewall/bugs/cvtofc_86/libdhf_rppt.so)
==64384==    by 0x40424E1: DHF_RGetObject (in /home/icewall/bugs/cvtofc_86/libdhf_rppt.so)
==64384==    by 0x403979E: FilterToHtml (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)
==64384==    by 0x4038AFB: DHF_GetHtml_V11 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)
==64384==    by 0x8049AF7: main (in /home/icewall/bugs/cvtofc_86/convert)

We see that a heap-based buffer overflow appears in the ParseEnvironment function during the memcpy operation. The buffer which is overflowed is also allocated in this function and its size is equal to 76 bytes. To stop the application execution when the overflow takes place we will use duma :

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x80 
EBX: 0xf7fcc000 --> 0x8e98 
ECX: 0xe020ed2c --> 0x720041 ('A')
EDX: 0xdff62000 --> 0x0 
ESI: 0x2cc 
EDI: 0x0 
EBP: 0xfffec6c8 --> 0xfffec6e8 --> 0xfffec718 --> 0xfffec788 --> 0xfffec7f8 --> 0xfffec888 --> 0xfffec8b8 --> 0xfffec8e8 --> 0xfffeccc8 --> 0xffffd038 --> 0x0 
ESP: 0xfffec6a0 --> 0x1 
EIP: 0xf7fc7df7 (mov    BYTE PTR [edx],al)
EFLAGS: 0x10296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xf7fc7def:	mov    ecx,DWORD PTR [ebp-0xc]
   0xf7fc7df2:	add    eax,ecx
   0xf7fc7df4:	movzx  eax,BYTE PTR [eax]
=> 0xf7fc7df7:	mov    BYTE PTR [edx],al
   0xf7fc7df9:	add    DWORD PTR [ebp-0x14],0x1
   0xf7fc7dfd:	mov    eax,DWORD PTR [ebp-0x14]
   0xf7fc7e00:	cmp    eax,DWORD PTR [ebp+0x10]
   0xf7fc7e03:	jb     0xf7fc7de4
[------------------------------------stack-------------------------------------]
0000| 0xfffec6a0 --> 0x1 
0004| 0xfffec6a4 --> 0x4c ('L')
0008| 0xfffec6a8 --> 0x0 
0012| 0xfffec6ac --> 0xff 
0016| 0xfffec6b0 --> 0x1 
0020| 0xfffec6b4 --> 0x4c ('L')
0024| 0xfffec6b8 --> 0xdff61fb4 --> 0x720041 ('A')
0028| 0xfffec6bc --> 0xe020ed2c --> 0x720041 ('A')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0xf7fc7df7 in _duma_memcpy (dest=0xdff61fb4, src=0xe020ed2c, size=0x2cc) at duma.c:2264
2264	    d[i] = s[i];
gdb-peda$ bt
#0  0xf7fc7df7 in _duma_memcpy (dest=0xdff61fb4, src=0xe020ed2c, size=0x2cc) at duma.c:2264
#1  0xf7fc8278 in memcpy (dest=0xdff61fb4, src=0xe020ed2c, size=0x2cc) at duma.c:2501
#2  0xf7bc33d3 in DMC_2OLEreadStream () from ./libdmc_comm.so
#3  0xf7fac004 in ParseEnvironment () from ./libdhf_rppt.so
#4  0xf7fa7f99 in ParseDocument () from ./libdhf_rppt.so
#5  0xf7fa7c6e in InitDocument () from ./libdhf_rppt.so
#6  0xf7fa64e2 in DHF_RGetObject () from ./libdhf_rppt.so
#7  0xf7fc179f in FilterToHtml () from ./libdhf_htmlif.so
#8  0xf7fc0afc in DHF_GetHtml_V11 () from ./libdhf_htmlif.so
#9  0x08049af8 in main ()
#10 0xf7d60af3 in __libc_start_main (main=0x8049730 <main>, argc=0x2, argv=0xffffd0d4, init=0x8049f70 <__libc_csu_init>, fini=0x8049f60 <__libc_csu_fini>, rtld_fini=0xf7feb160 <_dl_fini>, 
	stack_end=0xffffd0cc) at libc-start.c:287
#11 0x08048ad1 in _start ()
gdb-peda$ frame 3
#3  0xf7fac004 in ParseEnvironment () from ./libdhf_rppt.so
gdb-peda$ context
[----------------------------------registers-----------------------------------]
EAX: 0x80 
EBX: 0xf7fbb34c --> 0x1c24c 
ECX: 0xe020ed2c --> 0x720041 ('A')
EDX: 0xdff62000 --> 0x0 
ESI: 0xdff61fb4 --> 0x720041 ('A')
EDI: 0x4c ('L')
EBP: 0xfffec788 --> 0xfffec7f8 --> 0xfffec888 --> 0xfffec8b8 --> 0xfffec8e8 --> 0xfffeccc8 --> 0xffffd038 --> 0x0 
ESP: 0xfffec720 --> 0xe020ebd8 --> 0xe0212fc8 --> 0x7 
EIP: 0xf7fac004 (mov    dx,WORD PTR [ebp-0x4a])
EFLAGS: 0x10296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xf7fabffb:	push   esi
   0xf7fabffc:	push   DWORD PTR [ebp+0x8]
   0xf7fabfff:	call   0xf7fa2f6c <DMC_2OLEreadStream@plt>
=> 0xf7fac004:	mov    dx,WORD PTR [ebp-0x4a]
   0xf7fac008:	mov    eax,DWORD PTR [ebp-0x44]
   0xf7fac00b:	mov    WORD PTR [eax+0x4],dx
   0xf7fac00f:	inc    edx
   0xf7fac010:	mov    WORD PTR [ebp-0x4a],dx
[------------------------------------stack-------------------------------------]
0000| 0xfffec720 --> 0xe020ebd8 --> 0xe0212fc8 --> 0x7 
0004| 0xfffec724 --> 0xdff61fb4 --> 0x720041 ('A')
0008| 0xfffec728 --> 0x2c78 ('x,')
0012| 0xfffec72c --> 0x0 
0016| 0xfffec730 --> 0xdff55ff8 --> 0x3f2000f 
0020| 0xfffec734 --> 0xf7c1acac --> 0x5ebf4 
0024| 0xfffec738 --> 0xfffec750 --> 0x0 
0028| 0xfffec73c --> 0xcd6 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV

Reviewing the ParseEnvironment function in pseudo-code form we see the following:

Line 1 	_BOOL4 __cdecl ParseEnvironment(int a1, int a2, unsigned int a3, int a4)
Line 2 	{
Line 3 
Line 4 	  textMasterStyleAtom.offset0 = 0;
Line 5 	  textMasterStyleAtom.type = 0;
Line 6 	  textMasterStyleAtom.size = 0;
Line 7 	  if ( a3 )
Line 8 	  {
Line 9 		do
Line 10		{
Line 11		  PPT_SeekRecHeader(a1, &textMasterStyleAtom, 8, a4);
Line 12		  if ( LOWORD(textMasterStyleAtom.type) == 4003 )
Line 13		  {
Line 14			v7 = DMC_malloc(textMasterStyleAtom.size);
Line 15			DMC_2OLEreadStream(a1, v7, textMasterStyleAtom.size);
Line 16			ParseMasterDerivedStyle(v7, a2, &textMasterStyleAtom, a4);
Line 17			DMC_free(v7);
Line 18			goto LABEL_17;
Line 19		  }
Line 20		  (...)
Line 21			  fontEntityAtom.offset0 = 0;
Line 22			  fontEntityAtom.type = 0;
Line 23			  fontEntityAtom.size = 0;
Line 24			  PPT_SeekRecHeader(a1, &fontEntityAtom, 8, a4);
Line 25			  if ( LOWORD(fontEntityAtom.type) == 4023 )
Line 26			  {
Line 27				buffer = (void *)DMC_malloc(textMasterStyleAtom.size);
Line 28				DMC_2OLEreadStream(a1, buffer, fontEntityAtom.size);	  

The vulnerability occurs at line 28. At line 27 the allocation for buffer is made based on textMasterStyleAtom.size while at line 28 fontEntityAtom.size is used as a size argument for DMC_2OLEreadStream. Looking for the fontEntityAtom record in a file we can find it at offset 0x112C:

Offset 0 1 2 3 4 5 6 7 8 9 A B C D E F

00001120 00 00 B7 0F ..∑. 00001130 44 2F 00 00 D/..

So fontEntityAtom.size has a value of 0x2f44. We can also observe it under a debugger :

[----------------------------------registers-----------------------------------]
EAX: 0x81efa68 --> 0xf762b498 --> 0xf762b490 --> 0xf762b488 --> 0xf762b480 --> 0xf762b478 --> 0xf762b470 --> 0x81ab4c0 --> 0x0 
EBX: 0xf76f734c --> 0x1c24c 
ECX: 0xf762b420 --> 0x0 
EDX: 0x81efa68 --> 0xf762b498 --> 0xf762b490 --> 0xf762b488 --> 0xf762b480 --> 0xf762b478 --> 0xf762b470 --> 0x81ab4c0 --> 0x0 
ESI: 0x81efa68 --> 0xf762b498 --> 0xf762b490 --> 0xf762b488 --> 0xf762b480 --> 0xf762b478 --> 0xf762b470 --> 0x81ab4c0 --> 0x0 
EDI: 0x4c ('L')
EBP: 0xffb04af8 --> 0xffb04b68 --> 0xffb04bf8 --> 0xffb04c28 --> 0xffb04c58 --> 0xffb05038 --> 0xffb153a8 --> 0x0 
ESP: 0xffb04a90 --> 0x81f7a40 --> 0x81f0d60 --> 0x7 
EIP: 0xf76e7fff --> 0xff6f68e8
EFLAGS: 0x286 (carry PARITY adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xf76e7ff8:	push   DWORD PTR [ebp-0x30]
   0xf76e7ffb:	push   esi
   0xf76e7ffc:	push   DWORD PTR [ebp+0x8]
=> 0xf76e7fff:	call   0xf76def6c <DMC_2OLEreadStream@plt>
   0xf76e8004:	mov    dx,WORD PTR [ebp-0x4a]
   0xf76e8008:	mov    eax,DWORD PTR [ebp-0x44]
   0xf76e800b:	mov    WORD PTR [eax+0x4],dx
   0xf76e800f:	inc    edx
Guessed arguments:
arg[0]: 0x81f7a40 --> 0x81f0d60 --> 0x7 
arg[1]: 0x81efa68 --> 0xf762b498 --> 0xf762b490 --> 0xf762b488 --> 0xf762b480 --> 0xf762b478 --> 0xf762b470 --> 0x81ab4c0 --> 0x0 
arg[2]: 0x2f44 ('D/')

Where textMasterStyleAtom.size is located at offset 0x1129

Offset      0  1  2  3  4  5  6  7   8  9  A  B  C  D  E  F

00001120               0F 00 D5 07  4C 00                         ..’.L.

Since textMasterStyleAtom.size is 0x4C in this case, while fontEntityAtom.size this results in a heap-based buffer overflow that can lead to remote code execution.

Crash Information

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x80 
EBX: 0xf7fcc000 --> 0x8e98 
ECX: 0xe020ed2c --> 0x720041 ('A')
EDX: 0xdff62000 --> 0x0 
ESI: 0x2cc 
EDI: 0x0 
EBP: 0xfffec6c8 --> 0xfffec6e8 --> 0xfffec718 --> 0xfffec788 --> 0xfffec7f8 --> 0xfffec888 --> 0xfffec8b8 --> 0xfffec8e8 --> 0xfffeccc8 --> 0xffffd038 --> 0x0 
ESP: 0xfffec6a0 --> 0x1 
EIP: 0xf7fc7df7 (mov    BYTE PTR [edx],al)
EFLAGS: 0x10296 (carry PARITY ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xf7fc7def:	mov    ecx,DWORD PTR [ebp-0xc]
   0xf7fc7df2:	add    eax,ecx
   0xf7fc7df4:	movzx  eax,BYTE PTR [eax]
=> 0xf7fc7df7:	mov    BYTE PTR [edx],al
   0xf7fc7df9:	add    DWORD PTR [ebp-0x14],0x1
   0xf7fc7dfd:	mov    eax,DWORD PTR [ebp-0x14]
   0xf7fc7e00:	cmp    eax,DWORD PTR [ebp+0x10]
   0xf7fc7e03:	jb     0xf7fc7de4
[------------------------------------stack-------------------------------------]
0000| 0xfffec6a0 --> 0x1 
0004| 0xfffec6a4 --> 0x4c ('L')
0008| 0xfffec6a8 --> 0x0 
0012| 0xfffec6ac --> 0xff 
0016| 0xfffec6b0 --> 0x1 
0020| 0xfffec6b4 --> 0x4c ('L')
0024| 0xfffec6b8 --> 0xdff61fb4 --> 0x720041 ('A')
0028| 0xfffec6bc --> 0xe020ed2c --> 0x720041 ('A')
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0xf7fc7df7 in _duma_memcpy (dest=0xdff61fb4, src=0xe020ed2c, size=0x2cc) at duma.c:2264
2264	    d[i] = s[i];
gdb-peda$ bt
#0  0xf7fc7df7 in _duma_memcpy (dest=0xdff61fb4, src=0xe020ed2c, size=0x2cc) at duma.c:2264
#1  0xf7fc8278 in memcpy (dest=0xdff61fb4, src=0xe020ed2c, size=0x2cc) at duma.c:2501
#2  0xf7bc33d3 in DMC_2OLEreadStream () from ./libdmc_comm.so
#3  0xf7fac004 in ParseEnvironment () from ./libdhf_rppt.so
#4  0xf7fa7f99 in ParseDocument () from ./libdhf_rppt.so
#5  0xf7fa7c6e in InitDocument () from ./libdhf_rppt.so
#6  0xf7fa64e2 in DHF_RGetObject () from ./libdhf_rppt.so
#7  0xf7fc179f in FilterToHtml () from ./libdhf_htmlif.so
#8  0xf7fc0afc in DHF_GetHtml_V11 () from ./libdhf_htmlif.so
#9  0x08049af8 in main ()
#10 0xf7d60af3 in __libc_start_main (main=0x8049730 <main>, argc=0x2, argv=0xffffd0d4, init=0x8049f70 <__libc_csu_init>, fini=0x8049f60 <__libc_csu_fini>, rtld_fini=0xf7feb160 <_dl_fini>, 
	stack_end=0xffffd0cc) at libc-start.c:287
#11 0x08048ad1 in _start ()
gdb-peda$ exploitable 
Description: Access violation on destination operand
Short description: DestAv (9/29)
Hash: 62cee4b4cc752a3c4eeda5f3cd5127d2.47c08d0bf2e70b4c9c9140cb4bfb7a98
Exploitability Classification: EXPLOITABLE
Explanation: The target crashed on an access violation at an address matching the destination operand of the instruction. This likely indicates a write access violation, which means the attacker may control the write address and/or value.
Other tags: AccessViolation (28/29)

Timeline

2017-02-28 - Vendor Disclosure
2017-05-04 - Public Release

Credit

Discovered by Marcin 'Icewall' Noga of Cisco Talos.