Hancom Thinkfree NEO Hangul Word Processor HWPTAG_TAB_DEF Tab Count Code Execution Vulnerability(CVE-2017-2819)

2017-09-18T00:00:00
ID SSV:96510
Type seebug
Reporter Root
Modified 2017-09-18T00:00:00

Description

Summary

An exploitable heap-based buffer overflow exists in the Hangul Word Processor component (version 9.6.1.4350) of Hancom Thinkfree Office NEO 9.6.1.4902. A specially crafted document stream can cause an integer underflow resulting in a buffer overflow which can lead to code execution under the context of the application. An attacker can entice a user to open up a document in order to trigger this vulnerability.

Tested Versions

Thinkfree Office NEO Trial Word 9.6.1.4902 Hwp.exe Product Version: 9.6.1.4350 HwpApp.dll Product Version: 9.6.1.4350

Product URLs

  • http://www.hancom.com/global/product/productWindowsMain.do?gnb0=3&gnb1=4 * http://www.hancom.com/global/cs_center/csDownload.do

CVSSv3 Score

8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

CWE

CWE-122: Heap-based Buffer Overflow

Details

Thinkfree Office NEO is published by Hancom, Inc. and is considered one of the more popular office suites used within South Korea. Hancom's Word Processor utilizes a document format known as the Hangul Word Processing Document (.hwp) which is based on Microsoft's Structured Storage document format for encapsulating the different streams used by the document format. When processing an HWPTAGTABDEF record within the DocInfo stream, a signedness vulnerability leading to a heap overflow can be made to occur. The vulnerability occurs when calculating the number of tab definitions to grow an array in order to read data from the file into. If the number of tabs is signed, the application will fail to resize a buffer used to store each tab. Later when the application tries to read data from the file into said buffer, a buffer overflow will occur which will lead to code execution under the context of the application.

Hangul Word Processor utilizes the Structured Storage COM format to store the different components needed to describe a document in the Hangul Word Processing format. One of these streams is the FileHeader stream which contains a bit mask describing how the document is actually stored. One of these flags specifies that some of the streams are encoded using the LZW algorithm and thus need to be decompressed before processing. After determining whether the document is compressed or not, the application will proceed to process the DocInfo stream in order to store the general attributes that exist within the document. Although it is not necessary for this vulnerability, the BodyText stream will contain the actual contents of the document.

When processing the DocInfo stream, the application will begin by reading a few tags that are typically located near the beginning of the stream. The first tag is named HWPTAGDOCUMENTPROPERTIES(16), which has its header read at [1]. Immediately following this, the application will parse the HWPTAGIDMAPPINGS(17) record at [2] and use a loop to read a list of UINT32s out of the file [3] which are used for mapping identifiers. After parsing the list of ID-mappings, there is one more record which is read known as the HWPTAGBINDATA(18) record. Before the function call at [5], this record containing binary data is parsed at [4]. Finally at [5] the application calls a function which is used to process the font and tab information for the document. 0:008> u hwpapp+2a4c10 HwpApp!CHncAppShield::operator=+0xfe0d0: 6b254c10 6a10 push 10h 6b254c12 8bce mov ecx,esi 6b254c14 e827321400 call HwpApp!CHncAppShield::operator=+0x241300 (6b397e40) ; [1] HWPTAG_DOCUMENT_PROPERTIES record header 6b254c19 f6878402000008 test byte ptr [edi+284h],8 6b254c20 743b je HwpApp!CHncAppShield::operator=+0xfe11d (6b254c5d) ... HwpApp!CHncAppShield::operator=+0xfe1f9: 6b254d39 6a11 push 11h ; [2] HWPTAG_ID_MAPPINGS record header 6b254d3b 8bce mov ecx,esi 6b254d3d e8fe301400 call HwpApp!CHncAppShield::operator=+0x241300 (6b397e40) ... HwpApp!CHncAppShield::operator=+0xfe212: ; [3] HWPTAG_ID_MAPPINGS record data (list of dwords) 6b254d52 c70300000000 mov dword ptr [ebx],0 6b254d58 8bce mov ecx,esi 6b254d5a 8b06 mov eax,dword ptr [esi] 6b254d5c 6a04 push 4 6b254d5e 53 push ebx 6b254d5f ff5008 call dword ptr [eax+8] 6b254d62 83f804 cmp eax,4 6b254d65 7532 jne HwpApp!CHncAppShield::operator=+0xfe259 (6b254d99) ... 6b254d99 83c304 add ebx,4 6b254d9c ff8de8fdffff dec dword ptr [ebp-218h] 6b254da2 75ae jne HwpApp!CHncAppShield::operator=+0xfe212 (6b254d52) ... HwpApp!CHncAppShield::operator=+0xfe26b: 6b254dab ff4634 inc dword ptr [esi+34h] 6b254dae 8b8ff4000000 mov ecx,dword ptr [edi+0F4h] 6b254db4 e8d70dffff call HwpApp!CHncAppShield::operator=+0xef050 (6b245b90) ; [4] HWPTAG_BIN_DATA (header and data) 6b254db9 8d8ff8000000 lea ecx,[edi+0F8h] 6b254dbf e82c2dffff call HwpApp!CHncAppShield::operator=+0xf0fb0 (6b247af0) ; [5]

After calling the function at 6b247af0, the application will eventually execute the following function calls. The function call at [1] will be used to enumerate fonts that are on the system according to the list of font names that are specified within the document. These font names are identified by the HWPTAGFACENAME(19) records. Two more record types are parsed before the record type containing the vulnerability is parsed. These are the HWPTAGBORDERFILL(20) record at [2], followed by the HWPTAGCHARSHAPE(21) record at [3]. After the HWPTAGBORDERFILL(20) is allocated for and then HWPTAGCHARSHAPE(21) record's object is constructed, the function call at [4] will be used to parse the HWPTAGTABDEF(22) records defined within the document. After parsing HWPTABTABDEF(22) records, the HWPTAG_NUMBERING(23) records will also be parsed at [5]. 0:008> u hwpapp+297aff HwpApp!CHncAppShield::operator=+0xf0fbf: 6b247aff 8b06 mov eax,dword ptr [esi] 6b247b01 83c040 add eax,40h 6b247b04 50 push eax 6b247b05 e8c6090000 call HwpApp!CHncAppShield::operator=+0xf1990 (6b2484d0) ; [1] HWPTAG_FACE_NAME records 6b247b0a 8b06 mov eax,dword ptr [esi] 6b247b0c 8bce mov ecx,esi 6b247b0e 83c040 add eax,40h 6b247b11 50 push eax 6b247b12 e8690e0000 call HwpApp!CHncAppShield::operator=+0xf1e40 (6b248980) ; [2] HWPTAG_BORDER_FILL records 6b247b17 8b06 mov eax,dword ptr [esi] 6b247b19 8bce mov ecx,esi 6b247b1b 83c040 add eax,40h 6b247b1e 50 push eax 6b247b1f e80c100000 call HwpApp!CHncAppShield::operator=+0xf1ff0 (6b248b30) ; [3] HWPTAG_CHAR_SHAPE record 6b247b24 8b06 mov eax,dword ptr [esi] 6b247b26 8bce mov ecx,esi 6b247b28 83c040 add eax,40h 6b247b2b 50 push eax 6b247b2c e8af110000 call HwpApp!CHncAppShield::operator=+0xf21a0 (6b248ce0) ; [4] HWPTAG_TAB_DEF records 6b247b31 8b06 mov eax,dword ptr [esi] 6b247b33 8bce mov ecx,esi 6b247b35 83c040 add eax,40h 6b247b38 50 push eax 6b247b39 e8b2120000 call HwpApp!CHncAppShield::operator=+0xf22b0 (6b248df0) ; [5] HWPTAG_NUMBERING record

Inside the function 6b248ce0, the application will first calculate the terminator for the loop of HWPTAGTABDEF(22) records at [1] and then begin to enter the loop [2] in order to process each record. For each record the array that stores it will be grown via the function at [3]. This function will simply re-allocate the array that's stored at %esi+50. At [4], the application will read the header and contents of each HWPTAGTABDEF(22) record. 0:008> u hwpapp+298d4c HwpApp!CHncAppShield::operator=+0xf220c: 6b248d4c 33db xor ebx,ebx 6b248d4e 8b06 mov eax,dword ptr [esi] 6b248d50 8b80a8000000 mov eax,dword ptr [eax+0A8h] 6b248d56 8945ec mov dword ptr [ebp-14h],eax ; [1] loop sentinel 6b248d59 85c0 test eax,eax 6b248d5b 7e71 jle HwpApp!CHncAppShield::operator=+0xf228e (6b248dce) ... 6b248d5d 8d4900 lea ecx,[ecx] 6b248d60 ff7508 push dword ptr [ebp+8] ; [2] loop processing HWPTAG_TAB_DEF records 6b248d63 8d4dc8 lea ecx,[ebp-38h] 6b248d66 e8e50ae0ff call HwpApp!ResetCoreEngine+0x5eb10 (6b049850) ... 6b248d89 8d4e50 lea ecx,[esi+50h] 6b248d8c e83f080000 call HwpApp!CHncAppShield::operator=+0xf2a90 (6b2495d0) ; [3] grow HWPTAG_TAB_DEF array ... 6b248d91 8b4e54 mov ecx,dword ptr [esi+54h] ; index into HWPTAG_TAB_DEF array 6b248d94 43 inc ebx 6b248d95 8b4650 mov eax,dword ptr [esi+50h] 6b248d98 897c88fc mov dword ptr [eax+ecx*4-4],edi ; write into array 6b248d9c 3b5dec cmp ebx,dword ptr [ebp-14h] ; [1] loop sentinel being used to terminate loop 6b248d9f 7d1c jge HwpApp!CHncAppShield::operator=+0xf227d (6b248dbd) ... 6b248da1 8b75e8 mov esi,dword ptr [ebp-18h] 6b248da4 ebba jmp HwpApp!CHncAppShield::operator=+0xf2220 (6b248d60) ; [2] continue processing HWPTAG_TAB_DEF records

For each HWPTAGTABDEF(22) record in the file, the following function will be executed. When reading each record, its header is first processed at [1]. Each HWPTAGTABDEF(22) record begins with a UINT32 integer which describes properties of the tab. This is then followed by an integer which is used to contain the number of tabs defined within the record [2]. These tabs are aggregated in a single array which contains all of the tabs within each HWPTAGTABDEF(22) record within the file. The allocation which grows the array is done by the function call at [3]. Afterwards, the count for the single HWPTAGTABDEF(22) is multiplied by 8 and then passed to the function call at [4] to actually read each tab defined within the record. 0:008> u hwpapp+99850 HwpApp!ResetCoreEngine+0x5eb10: 6b049850 55 push ebp 6b049851 8bec mov ebp,esp 6b049853 51 push ecx 6b049854 56 push esi 6b049855 8b7508 mov esi,dword ptr [ebp+8] 6b049858 57 push edi 6b049859 8bf9 mov edi,ecx ... 6b04986d 6a16 push 16h ; [1] HWPTAG_TAB_DEF record header 6b04986f 8bce mov ecx,esi 6b049871 e8cae53400 call HwpApp!CHncAppShield::operator=+0x241300 (6b397e40) ... 6b04988c 51 push ecx 6b04988d ff7508 push dword ptr [ebp+8] ; [2] HWPTAG_TAB_DEF count used for allocation 6b049890 8d4f10 lea ecx,[edi+10h] 6b049893 e898b40000 call HwpApp!ResetCoreEngine+0x69ff0 (6b054d30) ; [3] Allocate array using the product of count and 8 at %edi+10 ... 6b0498a8 8b4508 mov eax,dword ptr [ebp+8] ; [2] HWPTAG_TAB_DEF count checked for zero 6b0498ab 85c0 test eax,eax 6b0498ad 746b je HwpApp!ResetCoreEngine+0x5ebda (6b04991a) 6b0498af 8b16 mov edx,dword ptr [esi] 6b0498b1 8bce mov ecx,esi 6b0498b3 c1e003 shl eax,3 ; [2] multiply HWPTAG_TAB_DEF count by 8 6b0498b6 50 push eax 6b0498b7 ff7710 push dword ptr [edi+10h] ; use buffer allocated at %edi+10 6b0498ba ff5208 call dword ptr [edx+8] ; [4] method to read specified data out of file

When allocating space for the number of tabs, the function call at 6b049893 is made which will check the current size of the tabs array and resize it if necessary. First at [1], the function will check to see if the number of tabs is 0. Next at [2], the application will check to see if the current buffer is unallocated. If so, then an allocation will be made. However, due to an HWPTAGTABDEF(22) already existing the application will continue executing so that it may layer resize the buffer. At [3], the application will check to see if the number of tabs is larger than the current count. Due to this comparison being signed, the number of tabs will be treated as less than the current count which will cause the application to not resize the buffer at [4]. The function then returns due to the application believing that there is no need to resize the buffer containing each tab. 0:008> u hwpapp+a4d30 HwpApp!ResetCoreEngine+0x69ff0: 6b054d30 55 push ebp 6b054d31 8bec mov ebp,esp 6b054d33 53 push ebx 6b054d34 56 push esi 6b054d35 57 push edi 6b054d36 8b7d08 mov edi,dword ptr [ebp+8] ; number of tabs 6b054d39 8bf1 mov esi,ecx 6b054d3b 85ff test edi,edi 6b054d3d 752c jne HwpApp!ResetCoreEngine+0x6a02b (6b054d6b) ; [1] check if number of tabs is defined ... 6b054d6b 8b16 mov edx,dword ptr [esi] 6b054d6d 85d2 test edx,edx 6b054d6f 7545 jne HwpApp!ResetCoreEngine+0x6a076 (6b054db6) ; [2] check if current buffer has been allocated ... 6b054db6 8b4e08 mov ecx,dword ptr [esi+8] 6b054db9 3bf9 cmp edi,ecx 6b054dbb 7f30 jg HwpApp!ResetCoreEngine+0x6a0ad (6b054ded) ; [3] check if current number of tabs is larger than current count 6b054dbd 8b4e04 mov ecx,dword ptr [esi+4] 6b054dc0 3bf9 cmp edi,ecx 6b054dc2 0f8eb3000000 jle HwpApp!ResetCoreEngine+0x6a13b (6b054e7b) ; [4] comparison which skips re-allocation ... 6b054e7b 897e04 mov dword ptr [esi+4],edi 6b054e7e b801000000 mov eax,1 6b054e83 5f pop edi 6b054e84 5e pop esi 6b054e85 5b pop ebx 6b054e86 5d pop ebp 6b054e87 c20800 ret 8

Returning back to the address after 6b049893, the application will then multiply the number of tags that were read by 8 and then pass it as an argument along with the buffer that was not resized to the method call at 6b0498ba in order to read tab data from the file into. Inside the method at 6b398340, the application will pass the destination buffer and its size at [1] to call a function at [2] which uses HncGZRead to decompress file data into a buffer. To perform this action, the application will enter a loop which will decompress each block from the stream into a buffer and then use memcpy to write data into the undersized buffer described previously [3]. Due to the application failing to resize the buffer due to a signed-ness issue, this memcpy will write outside the bounds of the heap buffer which will lead to a heap-based buffer overflow. ``` 0:008> u hwpapp+3e8340 HwpApp!CHncAppShield::operator=+0x241800: 6b398340 55 push ebp 6b398341 8bec mov ebp,esp 6b398343 56 push esi 6b398344 57 push edi 6b398345 8bf9 mov edi,ecx 6b398347 8b472c mov eax,dword ptr [edi+2Ch] 6b39834a 85c0 test eax,eax 6b39834c 743a je HwpApp!CHncAppShield::operator=+0x241848 (6b398388) ... 6b39835a 8b450c mov eax,dword ptr [ebp+0Ch] ; size 6b39835d 53 push ebx 6b39835e 8b5d08 mov ebx,dword ptr [ebp+8] ; destination 6b398361 3bc6 cmp eax,esi 6b398363 7e13 jle HwpApp!CHncAppShield::operator=+0x241838 (6b398378) ; [1] ... 6b398378 50 push eax ; size 6b398379 53 push ebx ; destination 6b39837a 8bcf mov ecx,edi 6b39837c e8efd3ffff call HwpApp!CHncAppShield::operator=+0x23ec30 (6b395770) ; \ [2] reads file data 6b398381 5b pop ebx 6b398382 5f pop edi 6b398383 5e pop esi 6b398384 5d pop ebp 6b398385 c20800 ret 8

0:008> u hwpapp+3e57cd HwpApp!CHncAppShield::operator=+0x23ec8d: 6b3957cd 3b1a cmp ebx,dword ptr [edx] 6b3957cf 8d450c lea eax,[ebp+0Ch] 6b3957d2 0f43c2 cmovae eax,edx 6b3957d5 8b30 mov esi,dword ptr [eax] ; read data from 6b3957d7 56 push esi ; length 6b3957d8 ff7710 push dword ptr [edi+10h] ; source file data 6b3957db 51 push ecx ; destination buffer 6b3957dc e82d781e00 call HwpApp!CHncAppShield::operator=+0x4264ce (6b57d00e) ; [3] memcpy ```

Crash Information

``` (5f4.6fc): Access violation - code c0000005 (first/second chance not available) eax=26d99280 ebx=80000010 ecx=000003ca edx=000004c2 esi=26d98eb6 edi=1f427000 eip=6e60f20c esp=2234ec24 ebp=2234ec4c iopl=0 nv up ei ng nz ac po cy cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010293 MSVCR120!memcpy+0x2a: 6e60f20c f3a4 rep movs byte ptr es:[edi],byte ptr [esi]

0:008> lm m hwp Browse full module list start end module name 00f20000 01476000 Hwp (deferred)

0:008> lm m hwpapp Browse full module list start end module name 6afb0000 6b8d5000 HwpApp (export symbols) HwpApp.dll

; Backtrace shows that the buffer being written to is 1f426f08 using the length of 4c2 bytes. 0:008> kv # ChildEBP RetAddr Args to Child
00 2234ec28 6b3957e1 1f426f08 26d98dbe 000004c2 MSVCR120!memcpy+0x2a (FPO: [3,0,2]) (CONV: cdecl) [f:\dd\vctools\crt\crtw32\string\i386\memcpy.asm @ 188] 01 2234ec4c 6b398381 1f426f08 80000010 00000002 HwpApp!CHncAppShield::operator=+0x23eca1 02 2234ec68 6b0498bd 1f426f08 80000010 00000001 HwpApp!CHncAppShield::operator=+0x241841 03 2234ec84 6b248d6b 90000002 69e27263 2234f038 HwpApp!ResetCoreEngine+0x5eb7d 04 2234ecdc 6b247b31 2234f078 2234f078 6b254dc4 HwpApp!CHncAppShield::operator=+0xf222b 05 2234ef28 6b2534de 2234f038 6b57af23 6e60ed79 HwpApp!CHncAppShield::operator=+0xf0ff1 06 2234ef8c 6b24d0d6 69e26eab 13c1c650 001dd928 HwpApp!CHncAppShield::operator=+0xfc99e 07 2234f014 6b24dcb3 001de578 001dd928 69e26e97 HwpApp!CHncAppShield::operator=+0xf6596 08 2234f51c 6affd05e 13c1c650 001de578 001dd928 HwpApp!CHncAppShield::operator=+0xf7173 09 2234fd98 6e565b7a 2234fdac 6e5658b8 001dd6f8 HwpApp!ResetCoreEngine+0x1231e 0a 2234fda0 6e5658b8 001dd6f8 2234fde4 6e62c01d HncBL90_6e560000!HncGetCurMainThreadID+0x2fa 0b 2234fdac 6e62c01d 001dd478 599bf348 00000000 HncBL90_6e560000!HncGetCurMainThreadID+0x38 0c 2234fde4 6e62c001 00000000 2234fdfc 74a4336a MSVCR120!_callthreadstartex+0x1b (FPO: [Non-Fpo]) (CONV: cdecl) [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 376] 0d 2234fdf0 74a4336a 22592c40 2234fe3c 76ed9902 MSVCR120!_threadstartex+0x7c (FPO: [Non-Fpo]) (CONV: stdcall) [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 354] 0e 2234fdfc 76ed9902 22592c40 54c66392 00000000 kernel32!BaseThreadInitThunk+0xe (FPO: [Non-Fpo]) 0f 2234fe3c 76ed98d5 6e62bfb4 22592c40 00000000 ntdll!__RtlUserThreadStart+0x70 (FPO: [Non-Fpo]) 10 2234fe54 00000000 6e62bfb4 22592c40 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])

; The size of the heap chunk at 1f426f08 is f8. This is a result of the total number of tabs being 1f then multiplied by 8 for the tab size 0:008> !heap -p -a 1f426f08 address 1f426f08 found in _DPH_HEAP_ROOT @ 381000 in busy allocation ( DPH_HEAP_BLOCK: UserAddr UserSize - VirtAddr VirtSize) 1f68057c: 1f426f08 f8 - 1f426000 2000 6e778e89 verifier!AVrfDebugPageHeapAllocate+0x00000229 76f70f3e ntdll!RtlDebugAllocateHeap+0x00000030 76f2ab47 ntdll!RtlpAllocateHeap+0x000000c4 76ed3431 ntdll!RtlAllocateHeap+0x0000023a 6e60ed63 MSVCR120!malloc+0x00000049 [f:\dd\vctools\crt\crtw32\heap\malloc.c @ 92] 6e60ee1f MSVCR120!operator new+0x0000001d [f:\dd\vctools\crt\crtw32\heap\new.cpp @ 59] 6b054d7e HwpApp!ResetCoreEngine+0x0006a03e 6b049898 HwpApp!ResetCoreEngine+0x0005eb58 6b248d6b HwpApp!CHncAppShield::operator=+0x000f222b 6b247b31 HwpApp!CHncAppShield::operator=+0x000f0ff1 6b2534de HwpApp!CHncAppShield::operator=+0x000fc99e 6b24d0d6 HwpApp!CHncAppShield::operator=+0x000f6596 6b24dcb3 HwpApp!CHncAppShield::operator=+0x000f7173 6affd05e HwpApp!ResetCoreEngine+0x0001231e 6e565b7a HncBL90_6e560000!HncGetCurMainThreadID+0x000002fa 6e5658b8 HncBL90_6e560000!HncGetCurMainThreadID+0x00000038 6e62c01d MSVCR120!_callthreadstartex+0x0000001b [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 376] 6e62c001 MSVCR120!_threadstartex+0x0000007c [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 354] 74a4336a kernel32!BaseThreadInitThunk+0x0000000e 76ed9902 ntdll!__RtlUserThreadStart+0x00000070 76ed98d5 ntdll!_RtlUserThreadStart+0x0000001b

; The source argument points to file contents 0:008> db 26d98dbe L20 26d98dbe 0c 1f 00 00 00 00 91 00-b0 91 00 00 00 03 00 00 ................ 26d98dce 19 04 a0 02 80 00 00 00-00 00 00 00 00 00 00 00 ................ ```

Exploit Proof-of-Concept

The Hangul Word Processor comonent utilizes the Compound Document format that is available via Microsoft's API. This file format is documented by Microsoft as part of their Document Interoperability Initiative. The format is similar to the FAT file format and contains a table describing where each file stream is stored within the file. Within each HWP file is a list of streams that are used to describe the document. This vulnerability is based around 2 streams, the FileHeader stream and the DocInfo stream.

Within the FileHeader stream is the following 512-byte structure. These fields describe the global format of the document. At the beginning of the stream is a 32-byte signature that contains the string "HWP Document File". Immediately following this is the version, which is 0x00000005. <class hangul.FileHeader> [0] <instance block(32) 'Signature'> "\x48\x57\x50\x20\x44\x6f\x63\x75\x6d\x65\x6e\x74\x20\x46\x69\x6c\x65\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" [20] <instance DWORD 'Version'> 0x05000000 (83886080) [24] <instance Flags 'Flags'> (0x00000001, 32) Compressed [28] <instance block(216) 'Reserved'> "\x00\x00\x00\x00\x00\x00\x00 ..skipped ~196 bytes.. \x00\x00\x00\x00\x00\x00\x00"

At offset 0x24 within the provided proof-of-concept is a 32-bit little-endian field that allows for one to describe whether streams are compressed using the LZW algorithm. If the last bit is set, then this specifies that the streams within the file are compressed with LZW excluding the header and CRC. <class hangul.Flags> 'Flags' [24.0] <instance 'Reserved'> (0x00000, 20) [26.4] <instance 'Has CCL'> (0x0, 1) [26.5] <instance 'DRM Document Certificate'> (0x0, 1) [26.6] <instance 'Has pre-stored Electronic Signature'> (0x0, 1) [26.7] <instance 'Has Certificate'> (0x0, 1) [27.0] <instance 'Has Signature'> (0x0, 1) [27.1] <instance 'Is Traceable'> (0x0, 1) [27.2] <instance 'XMLTemplate Storage'> (0x0, 1) [27.3] <instance 'Has DRM-Security'> (0x0, 1) [27.4] <instance 'Has Scripting'> (0x0, 1) [27.5] <instance 'For-distribution'> (0x0, 1) [27.6] <instance 'Has Password-protection'> (0x0, 1) [27.7] <instance 'Compressed'> (0x1, 1)

The other streams within the compound document format contain information about the text within the document. The DocInfo stream contains a list of tags which describe the type, length, and value of each record to be applied to the document. Within the provided proof-of-concept, the DocInfo stream is LZW compressed and will need to be decompressed in order to view the HWPTAGTABDEF(22) data structure that is responsible for this vulnerability. The HWPTAGTABDEF(22) structure that triggers the vulnerability is located at offset 0x5b2 within the decompressed stream. <class hangul.Record> '28' [5b2] <instance hangul.Type 'Type'> (0x01800416, 32) [5b6] <instance undefined 'Length'> ... [5b6] <instance hangul.HWPTAG_TAB_DEF 'Data'> "\x00\x00\x00\x00\x02\x00\x00\x90" [5be] <instance block(16) 'Padding'> "\x0c\x1f\x00\x00\x00\x00\x91\x00\xb0\x91\x00\x00\x00\x03\x00\x00"

At the beginning of each record is a 32-bit binary structure that describes the size and type of the record.. The first 12 bits describe the size, followed by 10-bits for the level (which is irrelevant to this vulnerability), and then 10 bits for the Identifier. If the size has all 12 of its bits set, a UINT32 that immediately follows will be used as the length. Within the proof-of-concept, the record has a size of 0x18 and an identifier of 0x16. <class hangul.Type> 'Type' [5b2.0] <instance 'Size'> (0x018, 12) [5b3.4] <instance 'Level'> (0x001, 10) [5b4.6] <instance 'Id'> (0x016, 10)

At offset 0x4b2 of the proof-of-concept is the data component of the first record. The HWPTAGTABDEF(22) describes information about the tab definitions within the document. The 2nd field (an INT32) is used as a count for the number of elements that follow and is multiplied by 8 before being used to allocate space for the tab data. <class hangul.HWPTAG_TAB_DEF> 'Data' [4b2] <instance hangul.UINT32 'Properties'> 0x00000000 (0) [4b6] <instance hangul.INT32 'Count'> 0x0000001f (31) [4ba] <instance dynamic.array(hangul.Tab,31) 'Tab'> hangul.Tab[31] "\x80\x1f\x00\x00\x00\x00\x91 ..skipped ~228 bytes.. \xf0\x03\x00\x00\x00\x00\x00"

At offset 0x5b6 is the data for the next tab definition which triggers the bug. If this value has its high bit set, then the heap buffer allocated in the previous tab definition will be reused without the array being resized to accommodate the space. In the proof-of-concept, the INT32 for the tab count is set to 0x90000002. <class hangul.HWPTAG_TAB_DEF> 'Data' [5b6] <instance UINT32 'Properties'> 0x00000000 (0) [5ba] <instance INT32 'Count'> -0x6ffffffe (-1879048190) [5be] <instance array(hangul.Tab,0) 'Tab'> hangul.Tab[0] "" The contents that are copied into the heap buffer then begin at offset 0x5be. 00005b0: 0000 1604 8001 0000 0000 0200 0090 0c1f ................ 00005c0: 0000 0000 9100 b091 0000 0003 0000 1904 ................ 00005d0: a002 8000 0000 0000 0000 0000 0000 0000 ................

Mitigation

Do not open HWP documents from untrusted sources.

Timeline

  • 2017–04-13 - Vendor Disclosure
  • 2017-05-12 - Public Release

CREDIT

  • Discovered by a member of Cisco Talos