Talos Vulnerability Report

TALOS-2017-0292

AntennaHouse DMC HTMLFilter AddSst Code Execution Vulnerability

May 4, 2017
CVE Number

CVE-2017-2799

Summary

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

Tested Versions

AntennaHouse DMC HTMLFilter shipped with MarkLogic 8.0-6
fb1a22fa08c986ec3614284f4e912b0a /opt/MarkLogic/Converters/cvtofc/libdhfrdoc.so
15b0acc464fba28335239f722a62037f /opt/MarkLogic/Converters/cvtofc/libdmc
comm.so
1eabb31236c675f9856a7d001b339334 /opt/MarkLogic/Converters/cvtofc/libdhfrxls.so
1415cbc784f05db0e9db424636df581a /opt/MarkLogic/Converters/cvtofc/libdhf
comm.so
4ae366fbd4540dd4c750e6679eb63dd4 /opt/MarkLogic/Converters/cvtofc/libdmcconf.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 XLS 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 XLS file can lead to heap corruption and ultimately to remote code execution.

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

[email protected]:~/bugs/cvtofc_86$ valgrind ./convert config_xls/
==18043== Memcheck, a memory error detector
==18043== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==18043== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==18043== Command: ./convert config_xls/
==18043== 
input=/home/icewall/bugs/cvtofc_86/config_xls/toconv.xls
output=/home/icewall/bugs/cvtofc_86/config_xls/conv.html
type=2
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=
==18043== Invalid write of size 1
==18043==    at 0x402F04B: memcpy (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==18043==    by 0x40471CE: AddSst (in /home/icewall/bugs/cvtofc_86/libdhf_rxls.so)
==18043==    by 0x4052960: GetWorkBookGlobal (in /home/icewall/bugs/cvtofc_86/libdhf_rxls.so)
==18043==    by 0x4052B8A: DHF_RGetObject (in /home/icewall/bugs/cvtofc_86/libdhf_rxls.so)
==18043==    by 0x403979E: FilterToHtml (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)
==18043==    by 0x4038AFB: DHF_GetHtml_V11 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)
==18043==    by 0x8049AF7: main (in /home/icewall/bugs/cvtofc_86/convert)
==18043==  Address 0x652306e is 0 bytes after a block of size 53,414 alloc'd
==18043==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==18043==    by 0x42DCCB1: DMC_malloc (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)
==18043==    by 0x4047054: AddSst (in /home/icewall/bugs/cvtofc_86/libdhf_rxls.so)
==18043==    by 0x4052960: GetWorkBookGlobal (in /home/icewall/bugs/cvtofc_86/libdhf_rxls.so)
==18043==    by 0x4052B8A: DHF_RGetObject (in /home/icewall/bugs/cvtofc_86/libdhf_rxls.so)
==18043==    by 0x403979E: FilterToHtml (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)
==18043==    by 0x4038AFB: DHF_GetHtml_V11 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)
==18043==    by 0x8049AF7: main (in /home/icewall/bugs/cvtofc_86/convert)

The heap overflow occurs in the AddSst function triggered by the memcpy, where the buffer related with this overflow is allocated in the same function. The pseudo-code of the most important part of this function looks as follows:

Line 1  signed int __cdecl AddSst(struct_a1_4 *a1)
Line 2  {
Line 3  (...)
Line 4      v4 = (unsigned __int16)Exc_GetWord((int)a1, &a1->dword128[index]);
Line 5  (...)
Line 6      a1->fooStruct[a1->i].buff = DMC_malloc(2 * v4);
Line 7      if ( !a1->fooStruct[a1->i].buff )
Line 8        return 12;
Line 9      memset((void *)a1->fooStruct[a1->i].buff, 0, 2 * v4);
Line 10 (...)
Line 11     n = 2 * v4;
Line 12     v21 = 0;
Line 13     while ( !(flag & 1) && index + n / 2 > a1->length || flag & 1 && n + index > a1->length )
Line 14     {
Line 15       if ( flag & 1 )
Line 16       {
Line 17         v11 = a1->length - index;
Line 18         a1->someSize = v11;
Line 19         memcpy((void *)(a1->fooStruct[a1->i].buff + v21), &a1->dword128[index], v11);
Line 20         v10 = a1->someSize;
Line 21       }

At line 4 the value for v4 is read directly from the file and later used for the allocation at line 6. The memcpy which causes the overflow is located at line 19. The problem occurs because the loop condition for the while loop at line 13 is wrong:

flag & 1 && n + index > a1->length

This leads to a situation where bigger index is subtracted from a smaller a1->length value at line 17. This results in an integer underflow and the resulting value is then stored in v11 and is used as a size argument in the memcpy at line 19. As a result the memcpy is attempting to copy a huge amount of data:

[----------------------------------registers-----------------------------------]
EAX: 0x88ca71f --> 0xc0000000 
EBX: 0xf7701c24 --> 0x1fb2c 
ECX: 0xfffffff9 
EDX: 0x8958de0 --> 0x0 
ESI: 0x8958de0 --> 0x0 
EDI: 0x88c9d10 --> 0x0 
EBP: 0xffa3b928 --> 0xffa3b958 --> 0xffa3b988 --> 0xffa3b9b8 --> 0xffa3bd98 --> 0xffa4c108 --> 0x0 
ESP: 0xffa3b8e0 --> 0x8958de0 --> 0x0 
EIP: 0xf76ee1ca --> 0xff7085e8
EFLAGS: 0x292 (carry parity ADJUST zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xf76ee1c7:  push   ecx
   0xf76ee1c8:  push   eax
   0xf76ee1c9:  push   edx
=> 0xf76ee1ca:  call   0xf76e5254 <[email protected]>
   0xf76ee1cf:  mov    eax,DWORD PTR [edi+0x134]
   0xf76ee1d5:  add    DWORD PTR [ebp-0x24],eax
   0xf76ee1d8:  sub    DWORD PTR [ebp-0x20],eax
   0xf76ee1db:  add    esp,0x10
Guessed arguments:
arg[0]: 0x8958de0 --> 0x0 
arg[1]: 0x88ca71f --> 0xc0000000 
arg[2]: 0xfffffff9 

The values come from the following locations in the file: a1->length - is the Length of an SST record located at 0x948. flag is coming from wrongly interpreted BoundSheet record located at 0x92E. Exactly one byte from the SheetName field which is "Sheet1" and its first letter "e" is interpreted as a flag. v4 is also coming from the aforrmentioned BoundSheet record. Its WORD size value also comes from SheetName string and is equal : 0x6853 ('Sh').

Crash Information

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x80a89bf --> 0x0 
EBX: 0xf7f06000 --> 0x1aada8 
ECX: 0xfffefcd9 --> 0x0 
EDX: 0x8137000 
ESI: 0x8126de0 --> 0xc0000000 
EDI: 0x8097d10 --> 0x0 
EBP: 0xfffec888 --> 0xfffec8b8 --> 0xfffec8e8 --> 0xfffec918 --> 0xfffeccf8 --> 0xffffd068 --> 0x0 
ESP: 0xfffec838 --> 0xf7fd1c24 --> 0x1fb2c 
EIP: 0xf7e8f2f9 (movntdq XMMWORD PTR [edx],xmm0)
EFLAGS: 0x10282 (carry parity adjust zero SIGN trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
   0xf7e8f2ea:  lea    eax,[eax+0x80]
   0xf7e8f2f0:  lfence 
   0xf7e8f2f3:  sub    ecx,0x80
=> 0xf7e8f2f9:  movntdq XMMWORD PTR [edx],xmm0
   0xf7e8f2fd:  movntdq XMMWORD PTR [edx+0x10],xmm1
   0xf7e8f302:  movntdq XMMWORD PTR [edx+0x20],xmm2
   0xf7e8f307:  movntdq XMMWORD PTR [edx+0x30],xmm3
   0xf7e8f30c:  movntdq XMMWORD PTR [edx+0x40],xmm4
[------------------------------------stack-------------------------------------]
0000| 0xfffec838 --> 0xf7fd1c24 --> 0x1fb2c 
0004| 0xfffec83c --> 0xf7fbe1cf (mov    eax,DWORD PTR [edi+0x134])
0008| 0xfffec840 --> 0x8126de0 --> 0xc0000000 
0012| 0xfffec844 --> 0x809871f --> 0xc0000000 
0016| 0xfffec848 --> 0xfffffff9 
0020| 0xfffec84c --> 0xf7fbdeb2 (pop    ebx)
0024| 0xfffec850 --> 0x80f62c0 --> 0x8090e08 --> 0x0 
0028| 0xfffec854 --> 0x8098710 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
__memcpy_ssse3_rep () at ../sysdeps/i386/i686/multiarch/memcpy-ssse3-rep.S:1294
1294    ../sysdeps/i386/i686/multiarch/memcpy-ssse3-rep.S: No such file or directory.
gdb-peda$ exploitable
Description: Access violation on destination operand
Short description: DestAv (9/29)
Hash: 2e2274428b15c89dbdab51192d9f77fb.49c0f84a1d8f1a6989793e98f9a59358
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)
gdb-peda$ bt
#0  __memcpy_ssse3_rep () at ../sysdeps/i386/i686/multiarch/memcpy-ssse3-rep.S:1294
#1  0xf7fbe1cf in AddSst () from ./libdhf_rxls.so
#2  0xf7fc9961 in GetWorkBookGlobal () from ./libdhf_rxls.so
#3  0xf7fc9b8b in DHF_RGetObject () from ./libdhf_rxls.so
#4  0xf7fd479f in FilterToHtml () from ./libdhf_htmlif.so
#5  0xf7fd3afc in DHF_GetHtml_V11 () from ./libdhf_htmlif.so
#6  0x08049af8 in main ()
#7  0xf7d74af3 in __libc_start_main (main=0x8049730 <main>, argc=0x2, argv=0xffffd104, init=0x8049f70 <__libc_csu_init>, fini=0x8049f60 <__libc_csu_fini>, rtld_fini=0xf7feb160 <_dl_fini>, 
    stack_end=0xffffd0fc) at libc-start.c:287
#8  0x08048ad1 in _start ()

Timeline

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

Credit

Discovered by Marcin 'Icewall' Noga of Cisco Talos.