ID CVE-2017-2792 Type cve Reporter cve@mitre.org Modified 2018-11-15T17:47:00
Description
An exploitable heap corruption vulnerability exists in the iBldDirInfo functionality of Antenna House DMC HTMLFilter used by MarkLogic 8.0-6. A specially crafted xls file can cause a heap corruption resulting in arbitrary code execution. An attacker can provide a malicious xls file to trigger this vulnerability.
{"seebug": [{"lastseen": "2017-11-19T12:02:43", "description": "### Summary\r\nAn exploitable heap corruption vulnerability exists in the iBldDirInfo functionality of AntennaHouse DMC HTMLFilter used by MarkLogic 8.0-6. A specially crafted xls file can cause a heap corruption resulting in arbitrary code execution. An attacker can provide a malicious xls file to trigger this vulnerability.\r\n\r\n### Tested Versions\r\nAntennaHouse DMC HTMLFilter shipped with MarkLogic 8.0-6\r\n```\r\nfb1a22fa08c986ec3614284f4e912b0a /opt/MarkLogic/Converters/cvtofc/libdhf_rdoc.so\r\n15b0acc464fba28335239f722a62037f /opt/MarkLogic/Converters/cvtofc/libdmc_comm.so\r\n1eabb31236c675f9856a7d001b339334 /opt/MarkLogic/Converters/cvtofc/libdhf_rxls.so\r\n1415cbc784f05db0e9db424636df581a /opt/MarkLogic/Converters/cvtofc/libdhf_comm.so\r\n4ae366fbd4540dd4c750e6679eb63dd4 /opt/MarkLogic/Converters/cvtofc/libdmc_conf.so\r\n81db1b55e18a0cb70a78410147f50b9c /opt/MarkLogic/Converters/cvtofc/libdhf_htmlif.so\r\nd716dd77c8e9ee88df435e74fad687e6 /opt/MarkLogic/Converters/cvtofc/libdhf_whtml.so\r\ne01d37392e2b2cea757a52ddb7873515 /opt/MarkLogic/Converters/cvtofc/convert\r\n```\r\n### Product URLs\r\nhttps://www.antennahouse.com/antenna1/\r\n\r\n### CVSSv3 Score\r\n8.3 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H\r\n\r\n### Details\r\nThis vulnerability is present in the AntennaHouse DMC HTMLFilter which is used, among others, to convert xls files to (x)html form.\r\nThis product is mainly used by MarkLogic for xls 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.\r\n\r\nLet's investigate this vulnerability. After execution of the XLS to html converter with a malformed xls file as an input we can easily observe the following when using Valgrind:\r\n```\r\nicewall@ubuntu:~/bugs/cvtofc_86$ valgrind ./convert config_xls/\r\n==43073== Memcheck, a memory error detector\r\n==43073== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.\r\n==43073== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info\r\n==43073== Command: ./convert config_xls/\r\n==43073== \r\ninput=/home/icewall/bugs/cvtofc_86/config_xls/toconv.xls\r\noutput=/home/icewall/bugs/cvtofc_86/config_xls/conv.html\r\ntype=2\r\ninfo.options='0'\r\n==43073== Invalid write of size 1\r\n==43073== at 0x42E012A: iBldDirInfo (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\r\n==43073== by 0x42DEDDA: DMC_2OLEopen (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\r\n==43073== by 0x42C0941: CommOLEOpen (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42BB8C9: autoOLE (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42BFD2C: detectFormat (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42C0269: DMC_FileDtct (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x4038144: DHF_GetFileInfo_V1 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)\r\n==43073== by 0x80498B4: main (in /home/icewall/bugs/cvtofc_86/convert)\r\n==43073== Address 0x4bedb90 is 0 bytes after a block of size 1,536 alloc'd\r\n==43073== at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)\r\n==43073== by 0x42DCCB1: DMC_malloc (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\r\n==43073== by 0x42DFFAD: iBldDirInfo (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\r\n==43073== by 0x42DEDDA: DMC_2OLEopen (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\r\n==43073== by 0x42C0941: CommOLEOpen (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42BB8C9: autoOLE (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42BFD2C: detectFormat (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42C0269: DMC_FileDtct (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x4038144: DHF_GetFileInfo_V1 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)\r\n==43073== by 0x80498B4: main (in /home/icewall/bugs/cvtofc_86/convert)\r\n==43073== \r\n==43073== Invalid write of size 1\r\n==43073== at 0x42E0151: iBldDirInfo (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\r\n==43073== by 0x42DEDDA: DMC_2OLEopen (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\r\n==43073== by 0x42C0941: CommOLEOpen (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42BB8C9: autoOLE (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42BFD2C: detectFormat (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42C0269: DMC_FileDtct (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x4038144: DHF_GetFileInfo_V1 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)\r\n==43073== by 0x80498B4: main (in /home/icewall/bugs/cvtofc_86/convert)\r\n==43073== Address 0x4bedb91 is 1 bytes after a block of size 1,536 alloc'd\r\n==43073== at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)\r\n==43073== by 0x42DCCB1: DMC_malloc (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\r\n==43073== by 0x42DFFAD: iBldDirInfo (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\r\n==43073== by 0x42DEDDA: DMC_2OLEopen (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\r\n==43073== by 0x42C0941: CommOLEOpen (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42BB8C9: autoOLE (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42BFD2C: detectFormat (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x42C0269: DMC_FileDtct (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\r\n==43073== by 0x4038144: DHF_GetFileInfo_V1 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)\r\n==43073== by 0x80498B4: main (in /home/icewall/bugs/cvtofc_86/convert)\r\n```\r\n\r\nWe see that a heap-based buffer overflow occurs in the function `iBldDirInfo`. The overflowed buffer is also allocated in this function. Running our target with an instrumented heap we stop in the following place :\r\n```\r\nProgram received signal SIGSEGV, Segmentation fault.\r\n[----------------------------------registers-----------------------------------]\r\nEAX: 0x93 \r\nEBX: 0xf7c1acac --> 0x5ebf4 \r\nECX: 0xe04a0fa0 --> 0x84938493 \r\nEDX: 0x60 ('`')\r\nESI: 0x60 ('`')\r\nEDI: 0x60 ('`')\r\nEBP: 0xfffec748 --> 0xfffec788 --> 0xfffec7b8 --> 0xfffec9b8 --> 0xfffec9d8 --> 0xfffeca08 --> 0xfffeccc8 --> 0xffffd038 --> 0x0 \r\nESP: 0xfffec640 --> 0xffffffff \r\nEIP: 0xf7bc412a (mov BYTE PTR [edx+ecx*1],al)\r\nEFLAGS: 0x10a06 (carry PARITY adjust zero sign trap INTERRUPT direction OVERFLOW)\r\n[-------------------------------------code-------------------------------------]\r\n 0xf7bc411d: movsx edx,si\r\n 0xf7bc4120: shr ax,0x8\r\n 0xf7bc4124: mov ecx,DWORD PTR [ebp-0xcc]\r\n=> 0xf7bc412a: mov BYTE PTR [edx+ecx*1],al\r\n 0xf7bc412d: inc esi\r\n 0xf7bc412e: movsx eax,si\r\n 0xf7bc4131: mov dl,BYTE PTR [ebp-0xfa]\r\n 0xf7bc4137: jmp 0xf7bc414b\r\n[------------------------------------stack-------------------------------------]\r\n0000| 0xfffec640 --> 0xffffffff \r\n0004| 0xfffec644 --> 0xffffffff \r\n0008| 0xfffec648 --> 0xffffffff \r\n0012| 0xfffec64c --> 0x93840000 \r\n0016| 0xfffec650 --> 0xe057e340 --> 0x0 \r\n0020| 0xfffec654 --> 0xe057cde0 --> 0xe049e000 --> 0xaaaaaaaa \r\n0024| 0xfffec658 --> 0xe049ee00 --> 0x1 \r\n0028| 0xfffec65c --> 0xe049f000 --> 0x0 \r\n[------------------------------------------------------------------------------]\r\nLegend: code, data, rodata, value\r\nStopped reason: SIGSEGV\r\n0xf7bc412a in iBldDirInfo () from ./libdmc_comm.so\r\ngdb-peda$ bt\r\n#0 0xf7bc412a in iBldDirInfo () from ./libdmc_comm.so\r\n#1 0xf7bc2ddb in DMC_2OLEopen () from ./libdmc_comm.so\r\n#2 0xf7c25942 in CommOLEOpen () from ./libdmc_dtct.so\r\n#3 0xf7c208ca in autoOLE () from ./libdmc_dtct.so\r\n#4 0xf7c24d2d in detectFormat () from ./libdmc_dtct.so\r\n#5 0xf7c2526a in DMC_FileDtct () from ./libdmc_dtct.so\r\n#6 0xf7fc0145 in DHF_GetFileInfo_V1 () from ./libdhf_htmlif.so\r\n#7 0x080498b5 in main ()\r\n#8 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>, \r\n stack_end=0xffffd0cc) at libc-start.c:287\r\n#9 0x08048ad1 in _start ()\r\n```\r\n\r\nTo figure out something more about the place where the out-of-bound write appears, we can review the pseudo code of the `iBldDirInfo` function:\r\n```\r\nLine 1 signed int __cdecl iBldDirInfo(struct_a1 *a1)\r\nLine 2 {\r\nLine 3 (...)\r\nLine 4 \r\nLine 5 v24 = 0;\r\nLine 6 v18 = -2;\r\nLine 7 v17 = 0;\r\nLine 8 v1 = (unsigned int)(a1->dword2C * a1->dword38) >> 2;\r\nLine 9 v21 = a1->dword38 / 128;\r\nLine 10 v23 = 1;\r\nLine 11 v2 = 0;\r\nLine 12 for ( i = lGetSect(a1, a1->dwordC); ; i = lGetSect(a1, v19) )\r\nLine 13 {\r\nLine 14 v19 = i;\r\nLine 15 if ( i == -1 )\r\nLine 16 break;\r\nLine 17 if ( i <= -2 )\r\nLine 18 {\r\nLine 19 v5 = v21 * v23;\r\nLine 20 a1->dword48 = v5;\r\nLine 21 v6 = DMC_malloc(96 * v5);\r\nLine 22 a1->buffer = v6;\r\nLine 23 if ( v6 )\r\nLine 24 {\r\nLine 25 v20 = a1->dwordC;\r\nLine 26 if ( !DMC_FileSeek(a1->pfile0, (v20 << a1->word8) + 512, 0) )\r\nLine 27 {\r\nLine 28 a1->word52 = 0;\r\nLine 29 elementIndex = 0;\r\nLine 30 while ( DMC_FileRead(rawDirectoryEntry, 1u, 0x80u, a1->pfile0) && elementIndex < a1->dword48 )\r\nLine 31 {\r\nLine 32 directoryEntry = (a1->buffer + 96 * elementIndex);\r\nLine 33 v7 = usGetShort(&v26);\r\nLine 34 if ( v7 )\r\nLine 35 {\r\nLine 36 directoryEntry->unsigned20 = v7;\r\nLine 37 ++a1->word52;\r\nLine 38 }\r\nLine 39 else\r\nLine 40 {\r\nLine 41 v24 = 1;\r\nLine 42 }\r\nLine 43 if ( v7 && !v24 )\r\nLine 44 {\r\nLine 45 memset(directoryEntry, 0, 0x20u);\r\nLine 46 v8 = 0;\r\nLine 47 index = 0;\r\nLine 48 directoryEntry->unsigned20;\r\nLine 49 while ( index <= 127 )\r\nLine 50 {\r\nLine 51 v10 = DMC_unicodetosjis(((rawDirectoryEntry[index + 1] << 8) | rawDirectoryEntry[index]));\r\nLine 52 v15 = v10;\r\nLine 53 if ( v10 )\r\nLine 54 {\r\nLine 55 v11 = v10 & 0xFF00;\r\nLine 56 if ( v11 )\r\nLine 57 directoryEntry->Name[v8++] = BYTE1(v11);\r\nLine 58 v12 = v8;\r\nLine 59 v13 = v15;\r\nLine 60 }\r\nLine 61 else\r\nLine 62 {\r\nLine 63 v12 = v8;\r\nLine 64 v13 = rawDirectoryEntry[index];\r\nLine 65 }\r\nLine 66 directoryEntry->Name[v12] = v13;\r\nLine 67 ++v8;\r\nLine 68 index += 2;\r\nLine 69(...)\r\n```\r\n\r\nThe out of bound write occurs at `line 57`. This code parses a `Compound File Directory Entry`, doing name conversion from Unicode to SJIS (Shift Japanese Industrial Standards) and fills dynamically allocated structure `directoryEntry` with the converted name. According to the documentation Directory Name should not be bigger than 64 bytes. We see a check for that in the while loop at `line 49`. The problem appears because instead of only increasing the counter `index` by 2 each time in the `while loop`, the developers also increase `v8` by 2 when a correct conversion took place from Unicode to SJIS. `We see v8` being incremented at lines `57` and `67`. `v8` as a `Directory Name` string index should not exceed 64 and should definitely not exceed 96 as is expected at `line 32`. As we can imagine, v8 can easly reach a value like 126 which occurs when:\r\n\r\nthere is still space for more `Directory Entry` structures (see allocation at `line 21`, `v5` equals the amount of `Directory Entries`), a write on buffer space dedicated for the next `Directory Entry` which will cause the current `Directory Entry` name to be overwritten during the next `Directory Entry` read.\r\n\r\nBut the most dangerous scenario appears when the parsed `Directory Entry` is the last one, writing outside of the `Name` buffer will cause a heap based buffer overflow, causing heap corruption which can lead to arbitrary code execution.\r\n\r\n### Crash Information\r\n```\r\nProgram received signal SIGABRT, Aborted.\r\n[----------------------------------registers-----------------------------------]\r\nEAX: 0x0 \r\nEBX: 0xab33 \r\nECX: 0xab33 \r\nEDX: 0x6 \r\nESI: 0x68 ('h')\r\nEDI: 0xf7f06000 --> 0x1aada8 \r\nEBP: 0xfffec698 --> 0x8095590 --> 0x84938493 \r\nESP: 0xfffec3d4 --> 0xfffec698 --> 0x8095590 --> 0x84938493 \r\nEIP: 0xf7fdacd9 (pop ebp)\r\nEFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)\r\n[-------------------------------------code-------------------------------------]\r\n 0xf7fdacd3: mov ebp,esp\r\n 0xf7fdacd5: sysenter \r\n 0xf7fdacd7: int 0x80\r\n=> 0xf7fdacd9: pop ebp\r\n 0xf7fdacda: pop edx\r\n 0xf7fdacdb: pop ecx\r\n 0xf7fdacdc: ret \r\n 0xf7fdacdd: and edi,edx\r\n[------------------------------------stack-------------------------------------]\r\n0000| 0xfffec3d4 --> 0xfffec698 --> 0x8095590 --> 0x84938493 \r\n0004| 0xfffec3d8 --> 0x6 \r\n0008| 0xfffec3dc --> 0xab33 \r\n0012| 0xfffec3e0 --> 0xf7d89687 (xchg ebx,edi)\r\n0016| 0xfffec3e4 --> 0xf7f06000 --> 0x1aada8 \r\n0020| 0xfffec3e8 --> 0xfffec484 --> 0x270f \r\n0024| 0xfffec3ec --> 0xf7d8cab3 (mov edx,DWORD PTR gs:0x8)\r\n0028| 0xfffec3f0 --> 0x6 \r\n[------------------------------------------------------------------------------]\r\nLegend: code, data, rodata, value\r\nStopped reason: SIGABRT\r\n0xf7fdacd9 in ?? ()\r\ngdb-peda$ exploitable\r\nDescription: Heap error\r\nShort description: HeapError (15/29)\r\nHash: eeae24290f4714292e59633fa30e2480.ff49ca1e4902951bc44eee297b21de88\r\nExploitability Classification: EXPLOITABLE\r\nExplanation: The target's backtrace indicates that libc has detected a heap error or that the target was executing a heap function when it stopped. This could be due to heap corruption, passing a bad pointer to a heap function such as free(), etc. Since heap errors might include buffer overflows, use-after-free situations, etc. they are generally considered exploitable.\r\nOther tags: AbortSignal (27/29)\r\n```\r\n\r\n### Timeline\r\n* 2017-02-09 - Vendor Disclosure\r\n* 2017-05-04 - Public Release\r\n\r\n### CREDIT\r\n* Discovered by Marcin 'Icewall' Noga of Cisco Talos.", "published": "2017-09-19T00:00:00", "type": "seebug", "title": "AntennaHouse DMC HTMLFilter iBldDirInfo Code Execution Vulnerability(CVE-2017-2792)", "bulletinFamily": "exploit", "cvelist": ["CVE-2017-2792"], "modified": "2017-09-19T00:00:00", "href": "https://www.seebug.org/vuldb/ssvid-96522", "id": "SSV:96522", "sourceData": "", "cvss": {"score": 0.0, "vector": "NONE"}, "sourceHref": ""}], "talos": [{"lastseen": "2020-07-01T21:25:27", "bulletinFamily": "info", "cvelist": ["CVE-2017-2792"], "description": "# Talos Vulnerability Report\n\n### TALOS-2017-0284\n\n## AntennaHouse DMC HTMLFilter iBldDirInfo Code Execution Vulnerability\n\n##### May 4, 2017\n\n##### CVE Number\n\nCVE-2017-2792 \n\n### Summary\n\nAn exploitable heap corruption vulnerability exists in the iBldDirInfo functionality of AntennaHouse DMC HTMLFilter used by MarkLogic 8.0-6. A specially crafted xls file can cause a heap corruption resulting in arbitrary code execution. An attacker can provide a malicious xls file to trigger this vulnerability.\n\n### Tested Versions\n\nAntennaHouse 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\n\n### Product URLs\n\n<https://www.antennahouse.com/antenna1/>\n\n### CVSSv3 Score\n\n8.3 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H\n\n### Details\n\nThis vulnerability is present in the AntennaHouse DMC HTMLFilter which is used, among others, to convert xls files to (x)html form. \nThis product is mainly used by MarkLogic for xls 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.\n\nLet\u2019s investigate this vulnerability. After execution of the XLS to html converter with a malformed xls file as an input we can easily observe the following when using Valgrind:\n \n \n icewall@ubuntu:~/bugs/cvtofc_86$ valgrind ./convert config_xls/\n ==43073== Memcheck, a memory error detector\n ==43073== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.\n ==43073== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info\n ==43073== Command: ./convert config_xls/\n ==43073== \n input=/home/icewall/bugs/cvtofc_86/config_xls/toconv.xls\n output=/home/icewall/bugs/cvtofc_86/config_xls/conv.html\n type=2\n info.options='0'\n ==43073== Invalid write of size 1\n ==43073== at 0x42E012A: iBldDirInfo (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\n ==43073== by 0x42DEDDA: DMC_2OLEopen (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\n ==43073== by 0x42C0941: CommOLEOpen (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42BB8C9: autoOLE (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42BFD2C: detectFormat (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42C0269: DMC_FileDtct (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x4038144: DHF_GetFileInfo_V1 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)\n ==43073== by 0x80498B4: main (in /home/icewall/bugs/cvtofc_86/convert)\n ==43073== Address 0x4bedb90 is 0 bytes after a block of size 1,536 alloc'd\n ==43073== at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)\n ==43073== by 0x42DCCB1: DMC_malloc (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\n ==43073== by 0x42DFFAD: iBldDirInfo (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\n ==43073== by 0x42DEDDA: DMC_2OLEopen (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\n ==43073== by 0x42C0941: CommOLEOpen (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42BB8C9: autoOLE (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42BFD2C: detectFormat (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42C0269: DMC_FileDtct (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x4038144: DHF_GetFileInfo_V1 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)\n ==43073== by 0x80498B4: main (in /home/icewall/bugs/cvtofc_86/convert)\n ==43073== \n ==43073== Invalid write of size 1\n ==43073== at 0x42E0151: iBldDirInfo (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\n ==43073== by 0x42DEDDA: DMC_2OLEopen (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\n ==43073== by 0x42C0941: CommOLEOpen (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42BB8C9: autoOLE (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42BFD2C: detectFormat (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42C0269: DMC_FileDtct (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x4038144: DHF_GetFileInfo_V1 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)\n ==43073== by 0x80498B4: main (in /home/icewall/bugs/cvtofc_86/convert)\n ==43073== Address 0x4bedb91 is 1 bytes after a block of size 1,536 alloc'd\n ==43073== at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)\n ==43073== by 0x42DCCB1: DMC_malloc (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\n ==43073== by 0x42DFFAD: iBldDirInfo (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\n ==43073== by 0x42DEDDA: DMC_2OLEopen (in /home/icewall/bugs/cvtofc_86/libdmc_comm.so)\n ==43073== by 0x42C0941: CommOLEOpen (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42BB8C9: autoOLE (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42BFD2C: detectFormat (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x42C0269: DMC_FileDtct (in /home/icewall/bugs/cvtofc_86/libdmc_dtct.so)\n ==43073== by 0x4038144: DHF_GetFileInfo_V1 (in /home/icewall/bugs/cvtofc_86/libdhf_htmlif.so)\n ==43073== by 0x80498B4: main (in /home/icewall/bugs/cvtofc_86/convert)\n \n\nWe see that a heap-based buffer overflow occurs in the function `iBldDirInfo`. The overflowed buffer is also allocated in this function. Running our target with an instrumented heap we stop in the following place :\n \n \n Program received signal SIGSEGV, Segmentation fault.\n [----------------------------------registers-----------------------------------]\n EAX: 0x93 \n EBX: 0xf7c1acac --> 0x5ebf4 \n ECX: 0xe04a0fa0 --> 0x84938493 \n EDX: 0x60 ('`')\n ESI: 0x60 ('`')\n EDI: 0x60 ('`')\n EBP: 0xfffec748 --> 0xfffec788 --> 0xfffec7b8 --> 0xfffec9b8 --> 0xfffec9d8 --> 0xfffeca08 --> 0xfffeccc8 --> 0xffffd038 --> 0x0 \n ESP: 0xfffec640 --> 0xffffffff \n EIP: 0xf7bc412a (mov BYTE PTR [edx+ecx*1],al)\n EFLAGS: 0x10a06 (carry PARITY adjust zero sign trap INTERRUPT direction OVERFLOW)\n [-------------------------------------code-------------------------------------]\n 0xf7bc411d:\tmovsx edx,si\n 0xf7bc4120:\tshr ax,0x8\n 0xf7bc4124:\tmov ecx,DWORD PTR [ebp-0xcc]\n => 0xf7bc412a:\tmov BYTE PTR [edx+ecx*1],al\n 0xf7bc412d:\tinc esi\n 0xf7bc412e:\tmovsx eax,si\n 0xf7bc4131:\tmov dl,BYTE PTR [ebp-0xfa]\n 0xf7bc4137:\tjmp 0xf7bc414b\n [------------------------------------stack-------------------------------------]\n 0000| 0xfffec640 --> 0xffffffff \n 0004| 0xfffec644 --> 0xffffffff \n 0008| 0xfffec648 --> 0xffffffff \n 0012| 0xfffec64c --> 0x93840000 \n 0016| 0xfffec650 --> 0xe057e340 --> 0x0 \n 0020| 0xfffec654 --> 0xe057cde0 --> 0xe049e000 --> 0xaaaaaaaa \n 0024| 0xfffec658 --> 0xe049ee00 --> 0x1 \n 0028| 0xfffec65c --> 0xe049f000 --> 0x0 \n [------------------------------------------------------------------------------]\n Legend: code, data, rodata, value\n Stopped reason: SIGSEGV\n 0xf7bc412a in iBldDirInfo () from ./libdmc_comm.so\n gdb-peda$ bt\n #0 0xf7bc412a in iBldDirInfo () from ./libdmc_comm.so\n #1 0xf7bc2ddb in DMC_2OLEopen () from ./libdmc_comm.so\n #2 0xf7c25942 in CommOLEOpen () from ./libdmc_dtct.so\n #3 0xf7c208ca in autoOLE () from ./libdmc_dtct.so\n #4 0xf7c24d2d in detectFormat () from ./libdmc_dtct.so\n #5 0xf7c2526a in DMC_FileDtct () from ./libdmc_dtct.so\n #6 0xf7fc0145 in DHF_GetFileInfo_V1 () from ./libdhf_htmlif.so\n #7 0x080498b5 in main ()\n #8 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>, \n \tstack_end=0xffffd0cc) at libc-start.c:287\n #9 0x08048ad1 in _start ()\n \n\nTo figure out something more about the place where the out-of-bound write appears, we can review the pseudo code of the `iBldDirInfo` function:\n \n \n Line 1 signed int __cdecl iBldDirInfo(struct_a1 *a1)\n Line 2 {\n Line 3 (...)\n Line 4 \n Line 5 v24 = 0;\n Line 6 v18 = -2;\n Line 7 v17 = 0;\n Line 8 v1 = (unsigned int)(a1->dword2C * a1->dword38) >> 2;\n Line 9 v21 = a1->dword38 / 128;\n Line 10 v23 = 1;\n Line 11 v2 = 0;\n Line 12 for ( i = lGetSect(a1, a1->dwordC); ; i = lGetSect(a1, v19) )\n Line 13 {\n Line 14 v19 = i;\n Line 15 if ( i == -1 )\n Line 16 break;\n Line 17 if ( i <= -2 )\n Line 18 {\n Line 19 v5 = v21 * v23;\n Line 20 a1->dword48 = v5;\n Line 21 v6 = DMC_malloc(96 * v5);\n Line 22 a1->buffer = v6;\n Line 23 if ( v6 )\n Line 24 {\n Line 25 v20 = a1->dwordC;\n Line 26 if ( !DMC_FileSeek(a1->pfile0, (v20 << a1->word8) + 512, 0) )\n Line 27 {\n Line 28 a1->word52 = 0;\n Line 29 elementIndex = 0;\n Line 30 while ( DMC_FileRead(rawDirectoryEntry, 1u, 0x80u, a1->pfile0) && elementIndex < a1->dword48 )\n Line 31 {\n Line 32 directoryEntry = (a1->buffer + 96 * elementIndex);\n Line 33 v7 = usGetShort(&v26);\n Line 34 if ( v7 )\n Line 35 {\n Line 36 directoryEntry->unsigned20 = v7;\n Line 37 ++a1->word52;\n Line 38 }\n Line 39 else\n Line 40 {\n Line 41 v24 = 1;\n Line 42 }\n Line 43 if ( v7 && !v24 )\n Line 44 {\n Line 45 memset(directoryEntry, 0, 0x20u);\n Line 46 v8 = 0;\n Line 47 index = 0;\n Line 48 directoryEntry->unsigned20;\n Line 49 while ( index <= 127 )\n Line 50 {\n Line 51 v10 = DMC_unicodetosjis(((rawDirectoryEntry[index + 1] << 8) | rawDirectoryEntry[index]));\n Line 52 v15 = v10;\n Line 53 if ( v10 )\n Line 54 {\n Line 55 v11 = v10 & 0xFF00;\n Line 56 if ( v11 )\n Line 57 directoryEntry->Name[v8++] = BYTE1(v11);\n Line 58 v12 = v8;\n Line 59 v13 = v15;\n Line 60 }\n Line 61 else\n Line 62 {\n Line 63 v12 = v8;\n Line 64 v13 = rawDirectoryEntry[index];\n Line 65 }\n Line 66 directoryEntry->Name[v12] = v13;\n Line 67 ++v8;\n Line 68 index += 2;\n Line 69(...)\n \n\nThe out of bound write occurs at `line 57`. This code parses a `Compound File Directory Entry`, doing name conversion from Unicode to SJIS (Shift Japanese Industrial Standards) and fills dynamically allocated structure `directoryEntry` with the converted name. According to the documentation `Directory Name` should not be bigger than 64 bytes. We see a check for that in the while loop at `line 49`. The problem appears because instead of only increasing the counter `index` by 2 each time in the `while loop`, the developers also increase `v8` by 2 when a correct conversion took place from Unicode to SJIS. `We see v8` being incremented at lines `57` and `67`. `v8` as a `Directory Name` string index should not exceed 64 and should definitely not exceed 96 as is expected at `line 32`. As we can imagine, `v8` can easly reach a value like 126 which occurs when:\n\n * there is still space for more `Directory Entry` structures (see allocation at `line 21`, `v5` equals the amount of `Directory Entries`), a write on buffer space dedicated for the next `Directory Entry` which will cause the current `Directory Entry` name to be overwritten during the next `Directory Entry` read.\n\nBut the most dangerous scenario appears when the parsed `Directory Entry` is the last one, writing outside of the `Name` buffer will cause a heap based buffer overflow, causing heap corruption which can lead to arbitrary code execution.\n\n### Crash Information\n \n \n Program received signal SIGABRT, Aborted.\n [----------------------------------registers-----------------------------------]\n EAX: 0x0 \n EBX: 0xab33 \n ECX: 0xab33 \n EDX: 0x6 \n ESI: 0x68 ('h')\n EDI: 0xf7f06000 --> 0x1aada8 \n EBP: 0xfffec698 --> 0x8095590 --> 0x84938493 \n ESP: 0xfffec3d4 --> 0xfffec698 --> 0x8095590 --> 0x84938493 \n EIP: 0xf7fdacd9 (pop ebp)\n EFLAGS: 0x206 (carry PARITY adjust zero sign trap INTERRUPT direction overflow)\n [-------------------------------------code-------------------------------------]\n 0xf7fdacd3:\tmov ebp,esp\n 0xf7fdacd5:\tsysenter \n 0xf7fdacd7:\tint 0x80\n => 0xf7fdacd9:\tpop ebp\n 0xf7fdacda:\tpop edx\n 0xf7fdacdb:\tpop ecx\n 0xf7fdacdc:\tret \n 0xf7fdacdd:\tand edi,edx\n [------------------------------------stack-------------------------------------]\n 0000| 0xfffec3d4 --> 0xfffec698 --> 0x8095590 --> 0x84938493 \n 0004| 0xfffec3d8 --> 0x6 \n 0008| 0xfffec3dc --> 0xab33 \n 0012| 0xfffec3e0 --> 0xf7d89687 (xchg ebx,edi)\n 0016| 0xfffec3e4 --> 0xf7f06000 --> 0x1aada8 \n 0020| 0xfffec3e8 --> 0xfffec484 --> 0x270f \n 0024| 0xfffec3ec --> 0xf7d8cab3 (mov edx,DWORD PTR gs:0x8)\n 0028| 0xfffec3f0 --> 0x6 \n [------------------------------------------------------------------------------]\n Legend: code, data, rodata, value\n Stopped reason: SIGABRT\n 0xf7fdacd9 in ?? ()\n gdb-peda$ exploitable\n Description: Heap error\n Short description: HeapError (15/29)\n Hash: eeae24290f4714292e59633fa30e2480.ff49ca1e4902951bc44eee297b21de88\n Exploitability Classification: EXPLOITABLE\n Explanation: The target's backtrace indicates that libc has detected a heap error or that the target was executing a heap function when it stopped. This could be due to heap corruption, passing a bad pointer to a heap function such as free(), etc. Since heap errors might include buffer overflows, use-after-free situations, etc. they are generally considered exploitable.\n Other tags: AbortSignal (27/29)\n \n\n### Timeline\n\n2017-02-09 - Vendor Disclosure \n2017-05-04 - Public Release\n\n##### Credit\n\nDiscovered by Marcin 'Icewall' Noga of Cisco Talos.\n\n* * *\n\nVulnerability Reports Next Report\n\nTALOS-2017-0279\n\nPrevious Report\n\nTALOS-2017-0286\n", "edition": 21, "modified": "2017-05-04T00:00:00", "published": "2017-05-04T00:00:00", "id": "TALOS-2017-0284", "href": "http://www.talosintelligence.com/vulnerability_reports/TALOS-2017-0284", "title": "AntennaHouse DMC HTMLFilter iBldDirInfo Code Execution Vulnerability", "type": "talos", "cvss": {"score": 6.8, "vector": "AV:N/AC:M/Au:N/C:P/I:P/A:P"}}]}