Talos Vulnerability Report


Hancom Hangul HCell CSSValFormat::CheckUnderbar Code Execution Vulnerability

August 4, 2016

Report ID



This vulnerability was discovered within the Hangul Hcell application which is part of the Hangul Office Suite. Hangul Office is published by Hancom, Inc. and is considered one of the more popular Office suites used within South Korea. When opening a Hangul Hcell Document (.cell) and processing a record that uses the CSSValFormat object, the application will search for an underscore (“_”) character at the end of the string and write a null terminator after it. If the character is at the very end of the string, the application will mistakenly write the null-byte outside the bounds of it’s destination. This can result in heap corruption that can lead code execution under the context of the application.

Tested Versions

Hancom Office 2014 VP Trial HCell.exe Product version: HCellApp.dll Product version: HCellBook.dll Product version:

Product Urls

http://www.hancom.com http://www.hancom.com/en/product/product2014vp_01.jsp

CVSSv3 Score

8.6 – CVSS:3.0/AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H


Hangul Hcell uses the Structured Storage COM API to load and store the Spreadsheet generated by a user. Although there are various streams that can be specified within a document, the Hcell application stores the contents of a workbook within the “Workbook” stream as an array of Type-Length-Value structures. Each of these structures describe the worksheets and the cells that compose the document. Within a document, a user can specify the format of any number of cells which will cause the application to convert the result of the cell’s contents to the format that the user has specified.

When parsing the Workbook stream, the application will perform 3 passes over each record. During the second pass, the application will collect the majority of the records containing formatting information as well as cell contents. If the record type is of the value 0x041e, then the application will switch to the case at 0x6ae774de. This block of code will call the method that is used to handle the 0x041e record type.

6ae774de 8b16            mov     edx,dword ptr [esi]
6ae774e0 8b4248          mov     eax,dword ptr [edx+48h]
6ae774e3 53              push    ebx
6ae774e4 8bce            mov     ecx,esi
6ae774e6 ffd0            call    eax        ; XXX: call function that loops over 0x041e records
6ae774e8 e984010000      jmp     HCellApp!CNexPTViewDefine::SetColLast+0x201f1 (6ae77671)

Inside this method call, the application will enter a loop that will iterate through each contiguous record of type 0x041e and execute the method at 0x6af14217 on it’s contents. This implies that it is common for the application to put a chain of these records next to each other. The method call at 0x6af14217 executes a function at address 0x6aee19f0 which will parse the contents of the 0x041e record type. It is prudent to note by the author that this vulnerability can be reached via any condition where the CSSValFormat::CheckUnderscore method is called and may be reachable through alternative file formats other than the 0x041e record type describe herein.

6af14200 56              push    esi
6af14201 57              push    edi
6af14202 8b7c240c        mov     edi,dword ptr [esp+0Ch]
6af14206 8b7714          mov     esi,dword ptr [edi+14h]
6af14210 8b07            mov     eax,dword ptr [edi]
6af14212 8b5020          mov     edx,dword ptr [eax+20h]
6af14215 8bcf            mov     ecx,edi
6af14217 ffd2            call    edx            ; XXX: handle the current record
6af14219 8b06            mov     eax,dword ptr [esi]
6af1421b 8b500c          mov     edx,dword ptr [eax+0Ch]
6af1421e 8bce            mov     ecx,esi
6af14220 ffd2            call    edx            ; read next record
6af14222 b91e040000      mov     ecx,41Eh
6af14227 663bc1          cmp     ax,cx
6af1422a 74e4            je      HCellApp!CHclUndoManager::AddRef+0xb2a0 (6af14210)

The function at 0x6aee19f0 will first read a uint16 from the file. This uint16 is used to determine the worksheet number that the CSSValFormat structure is to be attached to. After reading the worksheet number, the application will execute the function at 0x6aee1a54. This function will read a value format string from the contents of the record. After reading the value format string from the file, the application will pass this string to the HCellBook.dll!CSSValFormat::CheckUnderbar function at address 0x6aee1af4. This string is allocated with the size based on the contents of the first uint16 before the string contents begins.

6aee19f0 6aff            push    0FFFFFFFFh
6aee19f2 6804aa2d6b      push    offset HCellApp!CHclDoc::IsPrinting+0x22274 (6b2daa04)
6aee19f7 64a100000000    mov     eax,dword ptr fs:[00000000h]
6aee19fd 50              push    eax
6aee19fe 83ec2c          sub     esp,2Ch
6aee1a3c 6a02            push    2
6aee1a3e 8d542418        lea     edx,[esp+18h]
6aee1a42 52              push    edx
6aee1a43 ffd0            call    eax        ; read a uint16
6aee1a45 8b4e14          mov     ecx,dword ptr [esi+14h]
6aee1a48 8b11            mov     edx,dword ptr [ecx]
6aee1a4a 8b5248          mov     edx,dword ptr [edx+48h]
6aee1a4d 53              push    ebx
6aee1a4e 53              push    ebx
6aee1a4f 8d442428        lea     eax,[esp+28h]  ; string from file
6aee1a53 50              push    eax
6aee1a54 ffd2            call    edx        ; read a string-type
6aee1aed 8d442424        lea     eax,[esp+24h]  ; string from file
6aee1af1 50              push    eax
6aee1af2 8bcf            mov     ecx,edi
6aee1af4 ff15e053396b    call    dword ptr [HCellApp!CHclDoc::IsPrinting+0xdcc50 (6b3953e0)]    ; XXX: call CSSValFormat::CheckUnderbar(ushort *)

At the beginning of the CheckUnderbar function, the application will calculate the length of the widechar string. After finding the end of the string using this length, the application will then seek backwards looking for the last underscore. Once that is determined, the application will then rewrite the underscore followed by a null-terminator. If the string ends with an underscore, the application will write the underscore at the end of the string, followed by the null-terminator one byte after the end of the string. This writes outside the bounds of the string and if there’s an object contiguous to this string, can be used to corrupt the first byte of the structure that follows. If an object with a virtual table follows, this can be used to shift the methods of the virtual table into a state that can be used to cause further corruption.

6a6adde0 56              push    esi
6a6adde1 8b742408        mov     esi,dword ptr [esp+8]  ; string from file
6a6adde5 8bc6            mov     eax,esi
6a6adde7 8d5002          lea     edx,[eax+2]
6a6addea 8d9b00000000    lea     ebx,[ebx]
6a6addf0 668b08          mov     cx,word ptr [eax]
6a6addf3 83c002          add     eax,2
6a6addf6 6685c9          test    cx,cx
6a6addf9 75f5            jne     HCellBook!CSSValFormat::CheckUnderbar+0x10 (6a6addf0)
6a6addfb 2bc2            sub     eax,edx
6a6addfd d1f8            sar     eax,1
6a6ade06 66833c4e5f      cmp     word ptr [esi+ecx*2],5Fh   ; look for underscore
6a6ade0b 7505            jne     HCellBook!CSSValFormat::CheckUnderbar+0x32 (6a6ade12)
6a6ade0d 83e901          sub     ecx,1
6a6ade10 79f4            jns     HCellBook!CSSValFormat::CheckUnderbar+0x26 (6a6ade06)
6a6ade12 41              inc     ecx
6a6ade28 7410            je      HCellBook!CSSValFormat::CheckUnderbar+0x5a (6a6ade3a)
6a6ade2a b95f000000      mov     ecx,5Fh
6a6ade2f 33d2            xor     edx,edx
6a6ade31 66890c46        mov     word ptr [esi+eax*2],cx    ; write underscore
6a6ade35 6689544602      mov     word ptr [esi+eax*2+2],dx  ; XXX: write null byte

0:008> lm
start    end        module name
6aa10000 6b6f5000   HCellApp   (export symbols)       HCellApp.dll
6a620000 6a921000   HCellBook   (export symbols)       HCellBook.dll

Crash Analysis

(a94.a98): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0000002f ebx=00000000 ecx=0000005f edx=00000000 esi=0cb86fa0 edi=0cb82ee0
eip=6a6ade35 esp=002cd0c0 ebp=00000008 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
6a6ade35 6689544602      mov     word ptr [esi+eax*2+2],dx ds:0023:0cb87000=????

0:000> kv
ChildEBP RetAddr  Args to Child
002cd0c0 6aee1afa 0cb86fa0 57395f0d 002cd2b8 HCellBook!CSSValFormat::CheckUnderbar+0x55
002cd2d4 715f3c3a 5b3e74e5 6b628628 00000000 HCellApp!CHclUndoCommand::AddRef+0x5aada
002cd30c ffff0600 00051001 00000000 18798ff8 MSVCR90!free+0xec (FPO: [Non-Fpo])
002cd314 00000000 18798ff8 80030002 002cd3bc 0xffff0600

0:000> !heap -p -a @esi
    address 0cb86fa0 found in
    _DPH_HEAP_ROOT @ 3411000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                1e6111a0:          cb86fa0               60 -          cb86000             2000
    721b8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    777f628e ntdll!RtlDebugAllocateHeap+0x00000030
    777ba6cb ntdll!RtlpAllocateHeap+0x000000c4
    77785d20 ntdll!RtlAllocateHeap+0x0000023a
    715f3db8 MSVCR90!malloc+0x00000079
    715f3eb8 MSVCR90!operator new+0x0000001f
    710efe93 MSVCP90!std::_Allocate<unsigned short>+0x0000001a
    710f048f MSVCP90!std::allocator<unsigned short>::allocate+0x0000000f
    710f6245 MSVCP90!std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >::_Copy+0x0000005a
    710f62fd MSVCP90!std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >::_Grow+0x00000029
    710f6c86 MSVCP90!std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >::assign+0x00000042
    710f6cdc MSVCP90!std::basic_string<unsigned short,std::char_traits<unsigned short>,std::allocator<unsigned short> >::assign+0x0000001d
    6ae7bfe5 HCellApp!CNexPTViewDefine::SetColLast+0x00024b65
    6ae76c38 HCellApp!CNexPTViewDefine::SetColLast+0x0001f7b8
    6ae76155 HCellApp!CNexPTViewDefine::SetColLast+0x0001ecd5
    6ae364df HCellApp!CHclUndoCommand::GetCommandDesc+0x00004cef
    6ae2b47b HCellApp!ATL::CWindowImpl<CTxoRichEditCtrl,ATL::CWindow,ATL::CWinTraits<1442840576,0> >::GetWndCaption+0x0002e6cb
    6aad76b9 HCellApp!CHclDoc::Load+0x000001b9
    6aad6d10 HCellApp!CHclDoc::Load+0x00000cc0
    6aad5cee HCellApp!CHclDoc::OpenDocument+0x000002fe

0:000> ? @esi + @eax*2 >= cb86fa0+60
Evaluate expression: 0 = 00000000

0:000> ? @esi + @eax*2 + 2 >= cb86fa0+60
Evaluate expression: 1 = 00000001

0:000> dc @esi+@eax*2 -10 L8
0cb86fee  005f003f 003b0029 0028005f 005f0040  ?._.).;._.(.@._.
0cb86ffe  ???????? ???????? ???????? ????????  ????????????????

Description of the proof-of-concept

The Hangul HCell document format 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 Hcell file is a stream labeled “Worbook” which is where the contents of the document is stored at.

The overall structure of an Hcell document can be described as a list of arrays of smaller type-length-value structures. Each record within the stream is prefixed by a header described as the following.

struct {
    uint16 type
    uint16 size
    byte[size] data
} record

Each array is terminated by an element of type 0x000a. Within the proof-of-concept that should’ve been provided, this leaves a single substream for the worksheet. The element containing the CSSValFormat is identified by type 0x041e and is located at offset 0x6c6 of the provided proof-of-concept. This can also be identified as record 31 of the 1st stream. It is prudent to note that this vulnerability can be reached through a few different formats, records, or through regular document editting, but the record type 0x041e is the only record type known by the author that can be used to reach the code that triggers this vulnerabilty.

<class RecordGeneral> '31'
[6c6] <instance uint16_t 'type'> 0x041e (1054)
[6c8] <instance uint16_t 'length'> 0x0063 (99)
[6ca] <instance record041e 'data'> "\x2b\x00\x2f\x00\x00\x5f\x00  ..skipped ~79 bytes.. \x00\x28\x00\x40\x00\x5f\x00"

The 0x041e record’s contents begins at offset 0x6ca within the provided proof of concept. This record has the following structure. The format size field contains the number of widechars that follow. If the character that’s positioned at the end of the string according to the provided length is an underscore ‘_’, then the application will write the null-terminator outside the bounds of the space that was allocated for the CSSValFormat structure.

<class record041e>
[6ca] <instance uint16 'worksheet'> 0x002b (43)
[6cc] <instance uint16 'format size'> 0x002f (47)
[6ce] <instance block(98) 'format'> ???

The contents of the format field is a unicode wide-character string, and so is also represented within the provided proof-of-concept as such.

06ca  2b 00 2f 00 00 5f 00 28  00 2a 00 20 00 23 00 2c  +./.._.(.*. .#.,
06da  00 23 00 23 00 30 00 2e  00 31 36 30 00 30 00 5f  .#.#.0...160.0._
06ea  00 29 00 3b 00 5c 00 28  00 2a 00 20 00 23 00 2c  .).;.\.(.*. .#.,
06fa  00 23 00 23 00 30 00 2e  00 30 00 30 00 5c 00 29  .#.#.0...0.0.\.)
070a  00 3b 00 5f 00 28 00 2a  00 20 00 22 00 2d 00 22  .;._.(.*. .".-."
071a  00 3f 00 3f 00 5f 00 29  00 3b 00 5f 00 28 00 40  .?.?._.).;._.(.@
072a  00 5f 00                                          ._.
           `Last character is an underscore which triggers the vuln.


Discovered by Cisco Talos


2016-03-28 - Discovery
2016-04-19 - Vendor Notification
2016-08-04 - Public Disclosure