Talos Vulnerability Report

TALOS-2016-0150

Hancom Hangul HCell HncChart CFormulaTokenSizeModifier Code Execution Vulnerability

August 4, 2016
CVE Number

CVE-2016-4295

Description

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.

Tested Versions

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

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

Details

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

Crash Analysis

(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

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, 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.

Timeline

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

Credit

Discovered by Cisco Talos.