CVE-2016-4295
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 particular record within the Workbook stream, an index miscalculation leading to a heap overlow can be made to occur. The vulnerability occurs when processing data for a formula used to render a chart via the HncChartPlugin.hplg library. Due to a lack of bounds-checking when incrementing an index that is used for writing into a buffer for formulae, the application can be made to write pointer data outside it’s bounds which can lead to 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 wthin 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. The Hangul Hcell application includes a directory of plugins that are used to handle external features such as chart rendering or text art. This specific bug is depending on the HncChartPlugin.hplg plugin that is bundled with the Hangul Office Suite but is believed by the author to be reachable via any chart that uses the CFormulaTokenSizeModifier object to adjust formula.
When parsing the Workbook stream, the application will perform 3 passes over each record. The third pass is assumed by the author to parse record types that depend on cell data that is parsed during the second pass. Within the following code, the application checks that the record type is of type 0x7ef0. If the type matches this value, then the method call at 0x68858601 will occur with the current worksheet passed as an argument. This method is responsible for parsing the contents of the record.
0:005> u hcellapp+4684b9
HCellApp!CNexPTViewDefine::SetColLast+0x21039:
688584b9 3df07e0000 cmp eax,7EF0h
688584be 0f8f44010000 jg HCellApp!CNexPTViewDefine::SetColLast+0x21188 (68858608)
688584c4 0f842c010000 je HCellApp!CNexPTViewDefine::SetColLast+0x21176 (688585f6)
...
HCellApp!CNexPTViewDefine::SetColLast+0x21176:
688585f6 8b13 mov edx,dword ptr [ebx]
688585f8 8b82f4020000 mov eax,dword ptr [edx+2F4h]
688585fe 57 push edi ; worksheet
688585ff 8bcb mov ecx,ebx
68858601 ffd0 call eax ; XXX
68858603 e9bbfaffff jmp HCellApp!CNexPTViewDefine::SetColLast+0x20c43 (688580c3)
Near the beginning of the method, the application will read 2 bytes from the file. These 2 bytes are used to describe the chart type defined within the file. With the provided proof-of-concept, this type is 0x0067. The Chart Type of 0x0067 is used to describe an MSChartDoc object. This will cause the application to allocate a 0x168 byte structure utilized by the parser to store information related to the chart which will then get passed to the function call at address 0x6891b5f0.
0:005> u hcellapp+52b44a
HCellApp!CHclUndoManager::AddRef+0x324da:
6891b44a 8b4e14 mov ecx,dword ptr [esi+14h]
6891b44d 8b01 mov eax,dword ptr [ecx]
6891b44f 8b4004 mov eax,dword ptr [eax+4]
6891b452 bb02000000 mov ebx,2
6891b457 53 push ebx
6891b458 8d542418 lea edx,[esp+18h] ; Chart Type
6891b45c 52 push edx
6891b45d ffd0 call eax ; Read 2 bytes from the file
...
HCellApp!CHclUndoManager::AddRef+0x3251c:
6891b48c 0fb7442414 movzx eax,word ptr [esp+14h] ; Chart Type from file
6891b491 83f866 cmp eax,66h
6891b494 0f8fb1000000 jg HCellApp!CHclUndoManager::AddRef+0x325db (6891b54b)
...
HCellApp!CHclUndoManager::AddRef+0x325db:
6891b54b 83f867 cmp eax,67h
6891b54e 7440 je HCellApp!CHclUndoManager::AddRef+0x32620 (6891b590) ; XXX: type 0x0067
...
HCellApp!CHclUndoManager::AddRef+0x32620:
6891b590 6868010000 push 168h
6891b595 e836583800 call HCellApp!CHclDoc::IsPrinting+0x8640 (68ca0dd0) ; allocate space for chart instance
6891b59a 83c404 add esp,4
6891b59d 89442430 mov dword ptr [esp+30h],eax
6891b5a1 c644244007 mov byte ptr [esp+40h],7
6891b5a6 3bc7 cmp eax,edi
6891b5a8 7433 je HCellApp!CHclUndoManager::AddRef+0x3266d (6891b5dd)
...
HCellApp!CHclUndoManager::AddRef+0x32649:
6891b5b9 8b09 mov ecx,dword ptr [ecx]
6891b5bb 57 push edi
6891b5bc 8b7d08 mov edi,dword ptr [ebp+8]
6891b5bf 51 push ecx
6891b5c0 57 push edi
6891b5c1 50 push eax
6891b5c2 e86940e0ff call HCellApp!CHclConditionalFormatCondition::getCFType+0xab0 (6871f630) ; constructor for a Chart object
6891b5c7 8bd8 mov ebx,eax
...
HCellApp!CHclUndoManager::AddRef+0x32672:
6891b5e2 895c2430 mov dword ptr [esp+30h],ebx ; store chart object
6891b5e6 c644244008 mov byte ptr [esp+40h],8
6891b5eb 8b4e14 mov ecx,dword ptr [esi+14h]
6891b5ee 57 push edi
6891b5ef 51 push ecx
6891b5f0 e88b41e0ff call HCellApp!CHclConditionalFormatCondition::getCFType+0xc00 (6871f780) ; XXX: use the chart object
6891b5f5 85c0 test eax,eax
6891b5f7 742a je HCellApp!CHclUndoManager::AddRef+0x326b3 (6891b623)
This function call is responsible for reading properties of the chart from the file into the chart structure. Later within this function, the application will initialize an object on the stack that is used to store input from the file which is used to create a formula object. This code occurs at address 0x6863184f within the HCellApp.dll library. Later, this structure is passed as an argument (as well as the worksheet) to the function call at 686318c9 with the formulatype of 9.
0:005> u hcellapp+24184f
HCellApp!CHncAtlWindowImpl<CTxoRichEditCtrl,ATL::CWindow,ATL::CWinTraits<1442840576,0> >::CHncAtlWindowImpl<CTxoRichEditCtrl,ATL::CWindow,ATL::CWinTraits<1442840576,0> >+0x4a1f:
6863184f 33db xor ebx,ebx
68631851 33c0 xor eax,eax
68631853 33c9 xor ecx,ecx
68631855 895c241c mov dword ptr [esp+1Ch],ebx ; XXX: where object is initialized
...
68631871 895c2438 mov dword ptr [esp+38h],ebx ; file data
68631875 895c243c mov dword ptr [esp+3Ch],ebx ; chunk data
68631879 895c2440 mov dword ptr [esp+40h],ebx ; file data length
...
HCellApp!CHncAtlWindowImpl<CTxoRichEditCtrl,ATL::CWindow,ATL::CWinTraits<1442840576,0> >::CHncAtlWindowImpl<CTxoRichEditCtrl,ATL::CWindow,ATL::CWinTraits<1442840576,0> >+0x4a87:
686318b7 8b4808 mov ecx,dword ptr [eax+8] ; CWorkSheet
686318ba 8b4140 mov eax,dword ptr [ecx+40h] ; CBook
686318bd 6a09 push 9 ; CFormula:EFormulaType
686318bf 50 push eax
686318c0 8d542420 lea edx,[esp+20h] ; Formula Object
686318c4 52 push edx
686318c5 8d742428 lea esi,[esp+28h] ; Source Formula Structure
686318c9 e8228c1d00 call HCellApp!ATL::CWindowImpl<CTxoRichEditCtrl,ATL::CWindow,ATL::CWinTraits<1442840576,0> >::GetWndCaption+0x2d740 (6880a4f0) ; \
\
0:005> u hcellapp+41a535
HCellApp!ATL::CWindowImpl<CTxoRichEditCtrl,ATL::CWindow,ATL::CWinTraits<1442840576,0> >::GetWndCaption+0x2d76f:
6880a535 50 push eax ; CFormula:EFormulaType
6880a536 56 push esi ; source formula structure
6880a537 51 push ecx ; CBook
6880a538 57 push edi ; Stream
6880a539 52 push edx ; Result
6880a53a e861fcffff call HCellApp!ATL::CWindowImpl<CTxoRichEditCtrl,ATL::CWindow,ATL::CWinTraits<1442840576,0> >::GetWndCaption+0x2d3f0 (6880a1a0)
At the beginning of this function, the source formula structure that was initialized earlier is used to construct 2 different objects that inherit from a CFormulaTokenSizeModifier. This object is populated using data read from the file and is used to trigger the vulnerability. Later in the function, a 16-bit length is read from the file which is then used to allocate space for reading file data related to the CFormulaTokenSizeModifier object. The CFormulaTokenSizeModifier object is a 0xc068 byte object containing two pairs of large buffers within them. The first buffer is composed of 4096 words, and the second one (which is overflown by this vulnerability) is composed of 4096 dwords. This implies that there is a maximum of 4096 formulae that can be handled by the CFormulaTokenSizeModifier object.
0:005> u hcellapp+41a1cb
HCellApp!ATL::CWindowImpl<CTxoRichEditCtrl,ATL::CWindow,ATL::CWinTraits<1442840576,0> >::GetWndCaption+0x2d41b:
6880a1cb 8b7514 mov esi,dword ptr [ebp+14h] ; source formula structure
6880a1ce 8b7d0c mov edi,dword ptr [ebp+0Ch] ; stream
6880a1d1 8b4528 mov eax,dword ptr [ebp+28h]
6880a1d4 8b4d08 mov ecx,dword ptr [ebp+8]
6880a1d7 8b5d10 mov ebx,dword ptr [ebp+10h] ; CBook
6880a1da 50 push eax
6880a1db 51 push ecx ; Resulting 0xc068 CFormulaTokenSizeModifier
6880a1dc e8eff20200 call HCellApp!CNexPTViewDefine::SetColLast+0x2050 (688394d0) ; XXX: construct a CFormulaTokenSizeModifier structure and store to the first argument
...
HCellApp!ATL::CWindowImpl<CTxoRichEditCtrl,ATL::CWindow,ATL::CWinTraits<1442840576,0> >::GetWndCaption+0x2d488:
6880a238 8b4238 mov eax,dword ptr [edx+38h]
6880a23b 6824200000 push 2024h
6880a240 ffd0 call eax
6880a242 0fb7c8 movzx ecx,ax ; 16-bit length from leftover chunk
6880a245 51 push ecx
6880a246 6689462a mov word ptr [esi+2Ah],ax ; save 16-bit length
6880a24a e808394900 call HCellApp!CHclDoc::IsPrinting+0x53c7 (68c9db57) ; allocate space for rest of chunk
6880a24f 0fb74e2a movzx ecx,word ptr [esi+2Ah]
6880a253 83c404 add esp,4
6880a256 894624 mov dword ptr [esi+24h],eax
6880a259 8b17 mov edx,dword ptr [edi]
6880a25b 8b5204 mov edx,dword ptr [edx+4]
6880a25e 51 push ecx ; 16-bit length
6880a25f 50 push eax ; pointer to buffer
6880a260 8bcf mov ecx,edi
6880a262 ffd2 call edx ; read bytes from file into buffer
After the file data for the formula is read, this buffer along with the column and row is passed to the CFormulaTokenSizeModifier::ModifyFormula method at address 0x6883d000. This method is located within the HCellBook.dll shared library and contains the root of the vulnerability described herein.
0:005> u hcellapp+41a2a8
HCellApp!ATL::CWindowImpl<CTxoRichEditCtrl,ATL::CWindow,ATL::CWinTraits<1442840576,0> >::GetWndCaption+0x2d4f8:
6880a2a8 8b5524 mov edx,dword ptr [ebp+24h]
6880a2ab 52 push edx ; Column
6880a2ac 8b5520 mov edx,dword ptr [ebp+20h]
6880a2af 52 push edx ; Row
6880a2b0 8b551c mov edx,dword ptr [ebp+1Ch]
6880a2b3 52 push edx ; CWorkSheet
6880a2b4 8b5518 mov edx,dword ptr [ebp+18h]
6880a2b7 c745fc00000000 mov dword ptr [ebp-4],0
6880a2be 8b0b mov ecx,dword ptr [ebx]
6880a2c0 8b01 mov eax,dword ptr [ecx]
6880a2c2 8b402c mov eax,dword ptr [eax+2Ch]
6880a2c5 6a01 push 1
6880a2c7 52 push edx ; CFormula::EFormulaType
6880a2c8 0fb7562a movzx edx,word ptr [esi+2Ah]
6880a2cc 52 push edx ; rest of chunk
6880a2cd 8b5624 mov edx,dword ptr [esi+24h]
6880a2d0 52 push edx ; after file data
6880a2d1 0fb75628 movzx edx,word ptr [esi+28h]
6880a2d5 52 push edx ; file data length
6880a2d6 8b5620 mov edx,dword ptr [esi+20h]
6880a2d9 52 push edx ; file data
6880a2da ffd0 call eax ; \
\
0:005> u hcellapp+44cfe8
HCellApp!CNexPTViewDefine::SetColLast+0x5b68:
6883cfe8 8b442458 mov eax,dword ptr [esp+58h]
6883cfec 50 push eax ; CWorkSheet
6883cfed 8b44244c mov eax,dword ptr [esp+4Ch]
6883cff1 51 push ecx ; CFormula::EFormulaType
6883cff2 8b4c244c mov ecx,dword ptr [esp+4Ch]
6883cff6 52 push edx ; after file data length
6883cff7 8b54244c mov edx,dword ptr [esp+4Ch]
6883cffb 50 push eax ; after file data
6883cffc 51 push ecx ; file data length
6883cffd 52 push edx ; file data
6883cffe 8bce mov ecx,esi
6883d000 ff156c55d768 call dword ptr [HCellApp!CHclDoc::IsPrinting+0xdcddc (68d7556c)] ; call HCellBook!CFormulaTokenSizeModifier::ModifyFormula
Inside the ModifyFormula method is the following loop which processes data that is read from the file. This loop will continue until the end of the record data has been reached. Before the loop, the application will store a 32-bit pointer to the current position at %esi+3c. This pointer later gets incorrectly treated as a 16-bit value when determining the current position. It is prudent to note, that there’s more than one place that this pointer value will get incorrectly casted as a 16-bit value.
0:000> u hcellbook+3b180
HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x60:
6956b180 33c0 xor eax,eax
6956b182 8d5644 lea edx,[esi+44h] ; XXX: calculate pointer into buffer in CFormulaTokenSizeModifier
6956b185 89563c mov dword ptr [esi+3Ch],edx ; XXX: store it as a dword into the structure
6956b188 898644600000 mov dword ptr [esi+6044h],eax ; boolean flag
6956b18e 898648600000 mov dword ptr [esi+6048h],eax ; index
...
HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x80:
6956b1a0 8b4e04 mov ecx,dword ptr [esi+4]
6956b1a3 0fb601 movzx eax,byte ptr [ecx]
6956b1a6 41 inc ecx
6956b1a7 894e04 mov dword ptr [esi+4],ecx
6956b1aa 89460c mov dword ptr [esi+0Ch],eax
6956b1ad 3c80 cmp al,80h
6956b1af 730b jae HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x9c (6956b1bc)
...
HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x34d:
6956b46d 8b4e04 mov ecx,dword ptr [esi+4]
6956b470 3b4e08 cmp ecx,dword ptr [esi+8]
6956b473 0f8227fdffff jb HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x80 (6956b1a0)
At the beginning of this loop, the application will read a byte. This byte will be used to seek into an array labeled CFormulaBase::m_Bases. If the base returned is 0x19, then the application will read a word from the file followed by another byte. Laster, this next word will actually be used as a terminator to a smaller loop that is used to writing. Lastly the parser will check to see if the second bit of the byte is clear. At this point, the application will enter the loop that can be used for writing.
0:000> u hcellbook+3b1a0
HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x80:
6956b1a0 8b4e04 mov ecx,dword ptr [esi+4]
6956b1a3 0fb601 movzx eax,byte ptr [ecx] ; read byte from stream
6956b1a6 41 inc ecx
6956b1a7 894e04 mov dword ptr [esi+4],ecx
...
6956b1b1 0fb6d0 movzx edx,al
6956b1b4 8a8200cb6369 mov al,byte ptr HCellBook!CFormulaBase::m_Bases (6963cb00)[edx] ; seek into m_bases
6956b1ba eb02 jmp HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x9e (6956b1be)
...
6956b1be 0fb6c0 movzx eax,al
6956b1c1 83f83e cmp eax,3Eh
6956b1c4 894610 mov dword ptr [esi+10h],eax
6956b1c7 0f8dd3020000 jge HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x380 (6956b4a0)
6956b1cd 83f819 cmp eax,19h
6956b1d0 0f84e3000000 je HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x199 (6956b2b9) ; XXX
...
6956b2b9 8b4e04 mov ecx,dword ptr [esi+4]
6956b2bc 0fb74101 movzx eax,word ptr [ecx+1] ; read word
6956b2c0 8a11 mov dl,byte ptr [ecx] ; read byte
6956b2c2 0fb7d8 movzx ebx,ax ; loop length
6956b2c5 f6c202 test dl,2 ; check if 2nd bit is set
6956b2c8 7455 je HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x1ff (6956b31f) ; XXX
...
6956b31f f6c204 test dl,4
6956b322 0f84cf000000 je HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x2d7 (6956b3f7)
6956b328 0fb74903 movzx ecx,word ptr [ecx+3] ; read the next word
6956b32c 0fb7c3 movzx eax,bx ; loop length
This loop will iterate the number of times determined by the first 16-bit word that was read. For each iteration of this loop, the application will increase an index. This index will be used to write outside the bounds of the buffer allocated within the CFormulaTokenSizeModifier.
0:000> u hcellbook+3b387
HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x267:
6956b387 ba05000000 mov edx,5 ; start value to write with
6956b38c 8bd8 mov ebx,eax ; number of loop iterations
6956b38e 8bff mov edi,edi
... loop for %ebx iterations ...
6956b390 8b4604 mov eax,dword ptr [esi+4]
6956b393 0fb7ca movzx ecx,dx ; value to write
6956b396 0fb72c08 movzx ebp,word ptr [eax+ecx]
6956b39a 807c28ff19 cmp byte ptr [eax+ebp-1],19h
6956b39f 8d4428ff lea eax,[eax+ebp-1]
...
6956b3ed 83c202 add edx,2
6956b3f0 83eb01 sub ebx,1
6956b3f3 759b jne HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x270 (6956b390)
6956b3f5 eb6d jmp HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x344 (6956b464)
Due to a lack of bounds checking, the loop can allow for the index at %esi+6048 to be increased past the bounds of the CFormulaTokenSizeModifier’s second 4096 dword buffer. Once that occurs, then the write instruction at 0x6956b3da can then be used to write the value of %edx outside of the object leading to the buffer overflow.
0:000> u hcellbook+3b3ba
HCellBook!CFormulaTokenSizeModifier::ModifyFormula+0x29a:
6956b3ba 668b463c mov ax,word ptr [esi+3Ch] ; note: mis-cast of a pointer to a 16-bit word
6956b3be 662bc6 sub ax,si
6956b3c1 6683e840 sub ax,40h ; ax is now an index into the structure at %esi
6956b3c5 6689847e4c600000 mov word ptr [esi+edi*2+604Ch],ax ; use %edi to write into buffer
...
6956b3cd 8b463c mov eax,dword ptr [esi+3Ch] ; note: correct use of pointer
6956b3d0 8d4c0801 lea ecx,[eax+ecx+1]
6956b3d4 8b8648600000 mov eax,dword ptr [esi+6048h] ; XXX: read current index
6956b3da 898c864c800000 mov dword ptr [esi+eax*4+804Ch],ecx ; XXX: write outside bounds of the CFormulaTokenSizeModifier object
6956b3e1 ff8648600000 inc dword ptr [esi+6048h] ; increase the index
6956b3e7 8bbe48600000 mov edi,dword ptr [esi+6048h]
0:000> lm
683f0000 690d5000 HCellApp (export symbols) HCellApp.dll
69530000 69831000 HCellBook (export symbols) HCellBook.dll
(18c.a64): Access violation - code c0000005 (first/second chance not available)
eax=00003017 ebx=00005c4e ecx=06aeb03c edx=0000602d esi=06ae4f58 edi=00003017
eip=655ab3da esp=0021cb90 ebp=0000f900 iopl=0 nv up ei pl nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010202
HCellBook_65570000!CFormulaTokenSizeModifier::ModifyFormula+0x2ba:
655ab3da 898c864c800000 mov dword ptr [esi+eax*4+804Ch],ecx ds:0023:06af9000=????????
0:000> !heap -p -a @esi+804c
address 06aecfa4 found in
_HEAP @ 600000
HEAP_ENTRY Size Prev Flags UserPtr UserSize - state
06ae4f50 180d 0000 [00] 06ae4f58 0c05c - (busy)
? HCellApp!CNexPTViewDefine::`vftable'+1136c
0:000> ? @esi+@eax*4+804c
Evaluate expression: 112168960 = 06af9000
0:000> ? @esi+c05c
Evaluate expression: 112136116 = 06af0fb4
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, this leaves 2 substreams.
The HncChart element is identified by type 0x7ef0 and is located at offset 0x15a3 of the provided proof-of-concept. This can also be located as record 62 of the 2nd stream.
<class record> '62'
[15a3] <instance uint16_t 'type'> 0x7ef0 (32496)
[15a5] <instance uint16_t 'size'> 0x540e (21518)
[15a7] <instance record7ef0 'data'> "\x01\x00\x00\x00\x67\x00\x06 ..skipped ~21384 bytes.. \x00\x01\x00\x01\x00\x01"
Within this record, is the following structure. The first dword appears to be reserved, however the second field ChartType must correspond to the value described in the description at address 6891b44a. In the poc, it should be 0x0067.
<class record7ef0> 'data'
[15a7] <instance uint32 'reserved'> 0x00000001 (1)
[15ab] <instance uint16 'charttype'> 0x0067 (103)
[15ad] <instance charttype_67 'chartdata'> "\x06\x00\x06\x06\x00\x00\x39\x1e\x02\x00\x00\x00\x87\x12\x02\x00\xab\x1a\x0c\x00\x00\x00\x87\x12\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x0f\x00\x5b\x00\x00\x01\x00\x00\x00\x10\x00\x00\x00\x03\x00\x09\x00"
[15e3] <instance formuladata 'data'> "\x5c\x53\x00\x00\x00\x00\x00 ..skipped ~21324 bytes.. \x00\x01\x00\x01\x00\x01"
The chartdata record has the following structure and is terminated by a formula type that is prefixed by a 16-bit length. In order to get to the data pertaining to the vulnerability, this length will need to be used to seek past to the data that was modified.
<class charttype_67> 'chartdata'
[15ad] <instance Header_2 'chartheader'> "\x06\x00\x06\x06\x00\x00\x39\x1e\x02\x00\x00\x00\x87\x12\x02\x00\xab\x1a\x0c\x00\x00\x00\x87\x12\x00\x00"
[15c7] <instance uint16 '?'> 0x0000 (0)
[15c9] <instance uint16 '?'> 0x0000 (0)
[15cb] <instance uint8 'data_length'> 0x00 (0)
[15cc] <instance block(0) 'data'> ""
[15cc] <instance block(4) '??'> "\x00\x00\x00\x00"
[15d0] <instance uint16 '??'> 0x0011 (17)
[15d2] <instance wblock 'formula?'> "\x0f\x00\x5b\x00\x00\x01\x00\x00\x00\x10\x00\x00\x00\x03\x00\x09\x00"
After getting past the first formula that is parsed, the following data will be encountered. The loop that is used to read the next formulae is determined by the dword size at offset 0x15e3 of the stream.
<class data> 'formuladata'
[15e3] <instance uint32_t 'size'> 0x0000535c (21340)
[15e7] <instance block(21340) 'data'> "\x00\x00\x00\x00\x00\x00\x00 ..skipped ~21320 bytes.. \x00\x01\x00\x01\x00\x01"
At offset 0x693f, is the following structure. This controls the inputs that are used for the loop that can be used to write pointers to the formula outside of the 4096 dword buffer. Within the loop-data, each byte is checked to determine a path that should be taken. None of these values should be larger than 0x3e
struct {
uint32 reserved?
uint16 size
byte[size] loopdata
}
0006930: 0100 0400 0000 0400 0000 cfff 0100 0100 ................
0006940: 0100 0100 0b00 0000 0000 1904 ffff 0900 ................
To get to the write loop, each byte that is part of loopdata is checked. If a byte value of 19 is set in this data, then the next byte will be a flag. If the next flag has it’s 2nd bit cleared and it’s 4th bit set, then the write loop will be entered. In the proof-of-concept, this value is 04. Immediately after this flag is a 16-bit loop counter, which controls the number of iterations of the write loop. The rest of the bytes in the stream control a relative offset that is used by a cmp instruction and do not appear to be important to the vulnerability short of triggering an access violation.
,Flag
Formula Base 19. /
Byte data`\ \ | /`Loop Counter
0006940: 0100 0100 0b00 0000 0000 1904 ffff 0900 ................
0006950: 0000 0000 0000 0000 0000 0000 0000 0000 ................
If the number iterations of each loop accumulate the index near 4096, and then there’s a formula base of 19 which is used to write outside the bounds of the buffer, then this vulnerability is being triggered.
2016-03-28 - Discovery
2016-04-19 - Vendor Notification
2016-08-04 - Public Disclosure
Discovered by Cisco Talos.