CVE-2016-4296
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.
Hancom Office 2014 VP Trial HCell.exe Product version: 9.1.0.2176 HCellApp.dll Product version: 9.1.0.2176 HCellBook.dll Product version: 9.1.0.2176
http://www.hancom.com http://www.hancom.com/en/product/product2014vp_01.jsp
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.
HCellApp!CNexPTViewDefine::SetColLast+0x2005e:
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.
HCellApp!CHclUndoManager::AddRef+0xb290:
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.
HCellApp!CHclUndoCommand::AddRef+0x5a9d0:
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.
HCellBook!CSSValFormat::CheckUnderbar:
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
(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
HCellBook!CSSValFormat::CheckUnderbar+0x55:
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 ???????? ???????? ???????? ???????? ????????????????
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.
2016-03-28 - Discovery
2016-04-19 - Vendor Notification
2016-08-04 - Public Disclosure
Discovered by Cisco Talos.