Talos Vulnerability Report

TALOS-2018-0712

Atlantis Word Processor Huffman table code length remote code execution vulnerability

November 20, 2018
CVE Number

CVE-2018-4039

Summary

An exploitable out-of-bounds write vulnerability exists in the PNG implementation of Atlantis Word Processor, version 3.2.7.2. This can allow an attacker to corrupt memory, which can result in code execution under the context of the application. An attacker must convince a victim to open a specially crafted document in order to trigger this vulnerability.

Tested Versions

Atlantis Word Processor 3.2.7.1, 3.2.7.2

Product URLs

https://www.atlantiswordprocessor.com/en/

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-129: Improper Validation of Array Index

Details

Atlantis Word Processor is a traditional word processor that provides a number of useful features for a variety of users. The software is fully compatible with other word processors, such as Microsoft Office Word 2007, and even has a similar interface to Microsoft Word. Atlantis also has the ability to encrypt document files and fully customize the interface. This application is written in Delphi and contains the majority of its capabilities within a single relocatable binary.

When opening up a document that follows the open document format specification, the application will first fingerprint it in order to determine the correct file format parser which is performed by the following code. This code will first fetch the current TDoc object and then check one of its fields that represents the current file format enumeration. When open document format is selected, this field will have the value “3,” which results in the execution of case 3. At [1], the application will then execute a function that fingerprints and continues parsing of the document.

awp+0x1b3139:
005b3139 8b45e8          mov     eax,dword ptr [ebp-18h]    // TDoc
005b313c 8b80dc000000    mov     eax,dword ptr [eax+0DCh]   // TDoc.fileFormatEnumeration
005b3142 83f805          cmp     eax,5
005b3145 776a            ja      awp+0x1b31b1 (005b31b1)
005b3147 ff24854e315b00  jmp     dword ptr awp+0x1b314e (005b314e)[eax*4]
...
awp+0x1b3193:
005b3193 55              push    ebp                        // Case 3
005b3194 e8fbc0ffff      call    awp+0x1af294 (005af294)    // [1]
005b3199 59              pop     ecx
005b319a 8885d7f8ffff    mov     byte ptr [ebp-729h],al
005b31a0 eb1c            jmp     awp+0x1b31be (005b31be)

After determining the document type, the application will begin to parse it. When an image type is detected during parsing, the following function will be executed. This function will determine the image type and then construct an object based on this. When a PNG image is determined, the application will execute the branch at [2]. Immediately afterward at [3], the application will construct a TPNGImage object. Once the object has been constructed, the application will make a dynamic call at [4].

awp+0x179bcb:
00579bcb 53              push    ebx
00579bcc 56              push    esi
00579bcd 57              push    edi
00579bce 8bda            mov     ebx,edx
00579bd0 8bf0            mov     esi,eax
00579bd2 3a5e56          cmp     bl,byte ptr [esi+56h]
00579bd5 0f84fb000000    je      awp+0x179cd6 (00579cd6)
00579bdb 33c0            xor     eax,eax
00579bdd 55              push    ebp
00579bde 68cc9c5700      push    offset awp+0x179ccc (00579ccc)
00579be3 64ff30          push    dword ptr fs:[eax]
00579be6 648920          mov     dword ptr fs:[eax],esp
00579be9 84db            test    bl,bl
00579beb 7452            je      awp+0x179c3f (00579c3f)
...
00579c3f 8a4608          mov     al,byte ptr [esi+8]        // Image Type
00579c42 2c01            sub     al,1
00579c44 7204            jb      awp+0x179c4a (00579c4a)
00579c46 742d            je      awp+0x179c75 (00579c75)    // [2]
00579c48 eb48            jmp     awp+0x179c92 (00579c92)
...
00579c75 b201            mov     dl,1
00579c77 a1cc6b4300      mov     eax,dword ptr [awp+0x36bcc (00436bcc)]
00579c7c e82f99e9ff      call    awp+0x135b0 (004135b0)     // [3] Construct TPNGImage
00579c81 8bf8            mov     edi,eax
00579c83 897e48          mov     dword ptr [esi+48h],edi
00579c86 8b5610          mov     edx,dword ptr [esi+10h]
00579c89 8bc7            mov     eax,edi
00579c8b 8b08            mov     ecx,dword ptr [eax]
00579c8d ff5138          call    dword ptr [ecx+38h]        // [4] Process PNG Image
00579c90 eb1b            jmp     awp+0x179cad (00579cad)

The dynamic function that was called will execute the following code. This function is named TIBXSQLVAR::LoadFromFile and is used by each image type. This is a wrapper which will construct an instance of a TFileStream at [5]. This TFileStream object is then used to read from the image file when processing it. Immediately afterward, the application will then call a dynamic function to read the image from the TFileStream. The function _TPNGImage_LoadFromStream is called.

awp+0x11894:
00411894 55              push    ebp
00411895 8bec            mov     ebp,esp
00411897 51              push    ecx
00411898 53              push    ebx
00411899 8bd8            mov     ebx,eax
0041189b 6a20            push    20h
0041189d 8bca            mov     ecx,edx
0041189f a1d06b4000      mov     eax,dword ptr [awp+0x6bd0 (00406bd0)]
004118a4 b201            mov     dl,1
004118a6 e8b97dffff      call    awp+0x9664 (00409664)  // [5] Construct a TFileStream
004118ab 8945fc          mov     dword ptr [ebp-4],eax
004118ae 33c0            xor     eax,eax
004118b0 55              push    ebp
004118b1 68dc184100      push    offset awp+0x118dc (004118dc)
004118b6 64ff30          push    dword ptr fs:[eax]
004118b9 648920          mov     dword ptr fs:[eax],esp
004118bc 8b55fc          mov     edx,dword ptr [ebp-4]
004118bf 8bc3            mov     eax,ebx
004118c1 8b08            mov     ecx,dword ptr [eax]
004118c3 ff5140          call    dword ptr [ecx+40h]    // [6]
004118c6 33c0            xor     eax,eax

Inside the _TPNGImage_LoadFromStream function at 0x438d44, the following code will proceed to construct any required objects and then read each chunk into a list. To store each chunk, at [7] the application will construct a TChunkList object and then store it onto the stack. Following this, the application will read eight bytes for the PNG image signature and then compare it at [8]. Once the signature is verified, the application will then enter a loop at [9] that will construct each chunk type and append it to the current chunk list. Afterward at [10], the application will make sure to initialize the TChunkIHDR and TChunkIDAT objects. Once these objects have been successfully constructed, the application will then enter another loop that calls the function at [11] for each IDAT chunk that was parsed.

awp+0x38d44:
00438d44 55              push    ebp
00438d45 8bec            mov     ebp,esp
00438d47 83c4f4          add     esp,0FFFFFFF4h
....
00438d52 a1806b4300      mov     eax,dword ptr [awp+0x36b80 (00436b80)]
00438d57 e8d8feffff      call    awp+0x38c34 (00438c34)     // [7] Construct TChunkList
00438d5c 8945fc          mov     dword ptr [ebp-4],eax
...
00438d6d 8d55f4          lea     edx,[ebp-0Ch]
00438d70 b908000000      mov     ecx,8
00438d75 8bc3            mov     eax,ebx
00438d77 8b30            mov     esi,dword ptr [eax]
00438d79 ff5604          call    dword ptr [esi+4]          // Read 8 bytes from header
...
00438d7c ba1ca26600      mov     edx,offset awp+0x26a21c (0066a21c)
00438d81 8d45f4          lea     eax,[ebp-0Ch]
00438d84 b908000000      mov     ecx,8
00438d89 e84a27fdff      call    awp+0xb4d8 (0040b4d8)      // [8] @CompareMem
00438d8e 84c0            test    al,al
00438d90 7505            jne     awp+0x38d97 (00438d97)
...
awp+0x38d97:
00438d97 8bd3            mov     edx,ebx
00438d99 8b45fc          mov     eax,dword ptr [ebp-4]      // ChunkList
00438d9c e85ffdffff      call    awp+0x38b00 (00438b00)     // [9] Construct and append chunk
00438da1 84c0            test    al,al
00438da3 7414            je      awp+0x38db9 (00438db9)
00438da5 8bc3            mov     eax,ebx
00438da7 e81c07fdff      call    awp+0x94c8 (004094c8)      // TStream::GetPosition
00438dac 8bf0            mov     esi,eax
00438dae 8bc3            mov     eax,ebx
00438db0 e82f07fdff      call    awp+0x94e4 (004094e4)      // TStream::GetSize
00438db5 3bf0            cmp     esi,eax
00438db7 7cde            jl      awp+0x38d97 (00438d97)
...
00438db9 8b45fc          mov     eax,dword ptr [ebp-4]      // [10] ChunkList
00438dbc 8b5808          mov     ebx,dword ptr [eax+8]
00438dbf 837b0400        cmp     dword ptr [ebx+4],0
00438dc3 7427            je      awp+0x38dec (00438dec)
00438dc5 8bc3            mov     eax,ebx
00438dc7 e87cf0fcff      call    awp+0x7e48 (00407e48)      // Return first index of TList
00438dcc 8b15806a4300    mov     edx,dword ptr [awp+0x36a80 (00436a80)]
00438dd2 e8f99bfcff      call    awp+0x29d0 (004029d0)      // Construct TChunkIHDR
00438dd7 84c0            test    al,al
00438dd9 7411            je      awp+0x38dec (00438dec)
00438ddb 8b15d86a4300    mov     edx,dword ptr [awp+0x36ad8 (00436ad8)]
00438de1 8b45fc          mov     eax,dword ptr [ebp-4]
00438de4 e883fcffff      call    awp+0x38a6c (00438a6c)     // Construct TChunkIDAT
00438de9 40              inc     eax
00438dea 7505            jne     awp+0x38df1 (00438df1)
...
awp+0x38e02:
00438e02 8b45fc          mov     eax,dword ptr [ebp-4]      // Chunk list
00438e05 8b4008          mov     eax,dword ptr [eax+8]
00438e08 8bd6            mov     edx,esi
00438e0a e8fdeefcff      call    awp+0x7d0c (00407d0c)      // Return element from chunk list
00438e0f 8b10            mov     edx,dword ptr [eax]
00438e11 ff5204          call    dword ptr [edx+4]          // [11] TChunkIDAT
00438e14 46              inc     esi
00438e15 4b              dec     ebx
00438e16 75ea            jne     awp+0x38e02 (00438e02)

Upon calling the method belonging to TChunkIDAT, the function at 0x4379cc is then executed by the application. This function will construct a number of objects, one of which is the TZDecompressionStream at [12]. This object is then used to decompress the data belonging to an IDAT chunk. At [13], the application will then call a method belonging to TZDecompressionStream to decompress the contents of the data pointed to by the current stream. This function will simply enter a loop that reads data from the IDAT chunk within the file and then pass each chunk to the Inflate function call at [14] to decompress.

awp+0x379cc:
004379cc 53              push    ebx
004379cd 56              push    esi
004379ce 57              push    edi
004379cf 55              push    ebp
004379d0 81c4c0fbffff    add     esp,0FFFFFBC0h
004379d6 8bf0            mov     esi,eax
...
004379ea 8b4e08          mov     ecx,dword ptr [esi+8]
004379ed b201            mov     dl,1
004379ef a1f41e4300      mov     eax,dword ptr [awp+0x31ef4 (00431ef4)]
004379f4 e883ecffff      call    awp+0x3667c (0043667c)     // [12] Construct TZDecompressionStream
004379f9 89442420        mov     dword ptr [esp+20h],eax
...
awp+0x37ee4:
00437ee4 8d4f01          lea     ecx,[edi+1]                // Loop 
00437ee7 33c0            xor     eax,eax
00437ee9 8ac3            mov     al,bl
00437eeb 8b1484          mov     edx,dword ptr [esp+eax*4]
00437eee 8b442420        mov     eax,dword ptr [esp+20h]    // TZDecompressionStream
00437ef2 8b28            mov     ebp,dword ptr [eax]
00437ef4 ff5504          call    dword ptr [ebp+4]          // [13] \
...
00437f4e 80f301          xor     bl,1
00437f51 ff442410        inc     dword ptr [esp+10h]
00437f55 ff4c2428        dec     dword ptr [esp+28h]
00437f59 7589            jne     awp+0x37ee4 (00437ee4)
00437f5b eb05            jmp     awp+0x37f62 (00437f62)
\
awp+0x366e8:
004366e8 53              push    ebx
004366e9 56              push    esi
004366ea 57              push    edi
004366eb 51              push    ecx
004366ec 890c24          mov     dword ptr [esp],ecx
...
awp+0x36715:
00436715 837b1000        cmp     dword ptr [ebx+10h],0
00436719 755a            jne     awp+0x36775 (00436775)
...
awp+0x36764:
00436764 8d4330          lea     eax,[ebx+30h]
00436767 89430c          mov     dword ptr [ebx+0Ch],eax
0043676a 8b4304          mov     eax,dword ptr [ebx+4]
0043676d e8562dfdff      call    awp+0x94c8 (004094c8)      // TStream::GetPosition
00436772 894308          mov     dword ptr [ebx+8],eax
00436775 8d430c          lea     eax,[ebx+0Ch]
00436778 33d2            xor     edx,edx
0043677a e87df7ffff      call    awp+0x35efc (00435efc)     // [14] Call to Inflate
0043677f 85c0            test    eax,eax
00436781 7d05            jge     awp+0x36788 (00436788)
...
00436788 837b1c00        cmp     dword ptr [ebx+1Ch],0
0043678c 7f87            jg      awp+0x36715 (00436715)

The Inflate function at 0x435efc is used by the application to decompress data out of the PNG image. This function essentially is a large state machine that will process input data from the stream, and output as much data as possible before returning. At [15], a structure containing the current state is read from a property belonging to the TZDecompressionStream object. From this structure, the first byte contains the current mode which is then used to select a case in order to handle processing the input that is to be decompressed. When handling case 7 (CODELENS), this state is then passed to the function call at [16].

awp+0x35efc:
00435efc 53              push    ebx
00435efd 56              push    esi
00435efe 57              push    edi
00435eff 55              push    ebp
00435f00 8bfa            mov     edi,edx
00435f02 8bd8            mov     ebx,eax
...
00435f2c 8b4318          mov     eax,dword ptr [ebx+18h]    // [15] Inflate State
00435f2f 33d2            xor     edx,edx
00435f31 8a10            mov     dl,byte ptr [eax]          // Current mode
00435f33 83fa0d          cmp     edx,0Dh
00435f36 0f8721030000    ja      awp+0x3625d (0043625d)
00435f3c ff2495435f4300  jmp     dword ptr awp+0x35f43 (00435f43)[edx*4]
...
00435f7b 8bd3            mov     edx,ebx                    // Case 7
00435f7d 8b4014          mov     eax,dword ptr [eax+14h]
00435f80 8bce            mov     ecx,esi
00435f82 e869f2ffff      call    awp+0x351f0 (004351f0)     // [16]
00435f87 8bf0            mov     esi,eax
00435f89 83fefd          cmp     esi,0FFFFFFFDh
00435f8c 750d            jne     awp+0x35f9b (00435f9b)
00435f8e 8b4318          mov     eax,dword ptr [ebx+18h]
00435f91 c6000d          mov     byte ptr [eax],0Dh
00435f94 33d2            xor     edx,edx
00435f96 895004          mov     dword ptr [eax+4],edx
00435f99 eb91            jmp     awp+0x35f2c (00435f2c)

The following function call is used to process the different states that are available under the CODELENS case and is used specifically to build the different code and distance tables. Thus, this function is simply a large select statement that will branch based on the current state. At [16], this state will be fetched, and then used to determine the target address to branch to. When handling case 3, the application will allocate memory for the code lengths at [17]. During decoding, this array will be initialized with the different code lengths that were processed out of the input data. This implementation assumes that each of the code lengths that are written to this array are less than or equal to the value of 15. At [18] is the implementation of Case 5 which is used to build the code lengths table. This will take the current number of codes and adjust them before executing the block of code at [19]. The code at [19] will then set the initial lenbits and distbits before passing it to the actual function at [20] for processing.

awp+0x351f0:
004351f0 53              push    ebx
004351f1 56              push    esi
004351f2 57              push    edi
004351f3 55              push    ebp
004351f4 83c4d0          add     esp,0FFFFFFD0h
004351f7 890c24          mov     dword ptr [esp],ecx
...
00435235 33c0            xor     eax,eax
00435237 8a03            mov     al,byte ptr [ebx]          // [16]
00435239 83f809          cmp     eax,9
0043523c 0f87380b0000    ja      awp+0x35d7a (00435d7a)
00435242 ff248549524300  jmp     dword ptr awp+0x35249 (00435249)[eax*4]
...
awp+0x35777:
00435777 8b442404        mov     eax,dword ptr [esp+4]      // Length
0043577b c1e002          shl     eax,2
0043577e e8e9c6fcff      call    awp+0x1e6c (00401e6c)      // [17] System.GetMemory
00435783 89430c          mov     dword ptr [ebx+0Ch],eax
...
awp+0x358d0:
004358d0 8b4304          mov     eax,dword ptr [ebx+4]      // [18] Case 5
004358d3 89442404        mov     dword ptr [esp+4],eax      // Length
004358d7 8b442404        mov     eax,dword ptr [esp+4]      // Length
004358db 83e01f          and     eax,1Fh
004358de 0502010000      add     eax,102h
004358e3 8b542404        mov     edx,dword ptr [esp+4]      // Length
004358e7 c1ea05          shr     edx,5
004358ea 83e21f          and     edx,1Fh
004358ed 03c2            add     eax,edx
004358ef 3b4308          cmp     eax,dword ptr [ebx+8]
004358f2 0f8e15020000    jle     awp+0x35b0d (00435b0d)
...
awp+0x35b0d:
00435b0d 33c0            xor     eax,eax
00435b0f 894314          mov     dword ptr [ebx+14h],eax    // [19]
00435b12 c744241009000000 mov     dword ptr [esp+10h],9     // lenbits
00435b1a c744241406000000 mov     dword ptr [esp+14h],6     // distbits
00435b22 8b4304          mov     eax,dword ptr [ebx+4]
00435b25 89442404        mov     dword ptr [esp+4],eax      // length
00435b29 68feffff1f      push    1FFFFFFEh
00435b2e 8d442414        lea     eax,[esp+14h]
00435b32 50              push    eax                        // pointer to lenbits
00435b33 8d44241c        lea     eax,[esp+1Ch]
00435b37 50              push    eax                        // pointer to distbits
00435b38 8d442434        lea     eax,[esp+34h]
00435b3c 50              push    eax
00435b3d 8d44243c        lea     eax,[esp+3Ch]
00435b41 50              push    eax
00435b42 8b4324          mov     eax,dword ptr [ebx+24h]
00435b45 50              push    eax
00435b46 68feffff0f      push    0FFFFFFEh
00435b4b 57              push    edi
00435b4c 8b4b0c          mov     ecx,dword ptr [ebx+0Ch]    // codelens array
00435b4f 8b542424        mov     edx,dword ptr [esp+24h]    // length
00435b53 c1ea05          shr     edx,5
00435b56 83e21f          and     edx,1Fh
00435b59 42              inc     edx
00435b5a 8b442424        mov     eax,dword ptr [esp+24h]    // length
00435b5e 83e01f          and     eax,1Fh
00435b61 0501010000      add     eax,101h
00435b66 e805f4ffff      call    awp+0x34f70 (00434f70)     // [20]
00435b6b 89442404        mov     dword ptr [esp+4],eax

First, the function will store the pointers to the codelens array and the dists array. Next at [21], the application will allocate space for a work area when building the code table. This work area has enough space for 288 elements. This buffer will then be passed to the function at [21] along with some of the other required parameters in order to build the code length table using the input that was processed from the file. One of these required parameters is the codelens array. As mentioned previously, this implementation later assumes that none of these codes are larger than 15, despite the fact that there are no constraints on these values.

awp+0x34f70:
00434f70 55              push    ebp
00434f71 8bec            mov     ebp,esp
00434f73 83c4f4          add     esp,0FFFFFFF4h
00434f76 53              push    ebx
00434f77 56              push    esi
00434f78 57              push    edi
00434f79 894df8          mov     dword ptr [ebp-8],ecx              // codelens array
00434f7c 8955fc          mov     dword ptr [ebp-4],edx              // dists array
00434f7f 8bf8            mov     edi,eax
00434f81 33c0            xor     eax,eax
00434f83 8945f4          mov     dword ptr [ebp-0Ch],eax
00434f86 b880040000      mov     eax,480h                           // 288 elements
00434f8b e8dccefcff      call    awp+0x1e6c (00401e6c)              // [21] System.GetMemory
00434f90 8bf0            mov     esi,eax
...
awp+0x34f92:
00434f92 6801010000      push    101h                               // extra codes
00434f97 6830a06600      push    offset awp+0x26a030 (0066a030)     // lengthc
00434f9c 6a1e            push    1Eh                                // length of lengthc
00434f9e 68aca06600      push    offset awp+0x26a0ac (0066a0ac)     // lengtheb
00434fa3 6a1e            push    1Eh                                // length of lengtheb
00434fa5 8b4518          mov     eax,dword ptr [ebp+18h]
00434fa8 50              push    eax
00434fa9 8b4520          mov     eax,dword ptr [ebp+20h]            // pointer to lenbits
00434fac 50              push    eax
00434fad 8b4510          mov     eax,dword ptr [ebp+10h]
00434fb0 50              push    eax
00434fb1 8b450c          mov     eax,dword ptr [ebp+0Ch]            // value of 0xffffffe
00434fb4 50              push    eax
00434fb5 8d45f4          lea     eax,[ebp-0Ch]                      // pointer to codelens array
00434fb8 50              push    eax
00434fb9 56              push    esi
00434fba 68feffff1f      push    1FFFFFFEh
00434fbf 8b45f8          mov     eax,dword ptr [ebp-8]              // codelens array
00434fc2 8bcf            mov     ecx,edi
00434fc4 8b5524          mov     edx,dword ptr [ebp+24h]            // value of 0x1ffffffe
00434fc7 e824fbffff      call    awp+0x34af0 (00434af0)             // [22] inflate_table
00434fcc 8bd8            mov     ebx,eax

The inflate_table function at 0x434af0 is then responsible for containing our vulnerability. First, this function copies the current number of codes and the lens array into variables on the stack. Once this has been done, at [23], a 64-byte array will be initialized to contain the different counts. Afterward, the application will then update this array using the current code lengths that were passed to it via the %eax register. Due to the application not checking that these code lengths are larger than 15, the loop at [24] can potentially write out of bounds of this 64-byte array. If any of the code lengths are larger than 15, then the instruction at [25] will write outside the bounds of its target array. This can cause memory corruption, which can lead to code execution under the context of the application.

awp+0x34af0:
00434af0 55              push    ebp
00434af1 8bec            mov     ebp,esp
00434af3 81c408ffffff    add     esp,0FFFFFF08h
00434af9 53              push    ebx
00434afa 56              push    esi
00434afb 57              push    edi
00434afc 894df4          mov     dword ptr [ebp-0Ch],ecx    // codes
00434aff 8955f8          mov     dword ptr [ebp-8],edx
00434b02 8945fc          mov     dword ptr [ebp-4],eax      // lens
...
00434b05 8d4584          lea     eax,[ebp-7Ch]              // count array
00434b08 ba40000000      mov     edx,40h
00434b0d e87218fdff      call    awp+0x6384 (00406384)      // [23] bzero
...
00434b12 8b4df4          mov     ecx,dword ptr [ebp-0Ch]    // codes
00434b15 49              dec     ecx
00434b16 85c9            test    ecx,ecx
00434b18 7210            jb      awp+0x34b2a (00434b2a)
00434b1a 41              inc     ecx
00434b1b 8b55fc          mov     edx,dword ptr [ebp-4]      // lens
...
awp+0x34b1e:                                                // [24] loop to update count table
00434b1e 8b02            mov     eax,dword ptr [edx]        // lens[index]
00434b20 ff448584        inc     dword ptr [ebp+eax*4-7Ch]  // [25] count[%eax]++
00434b24 83c204          add     edx,4
00434b27 49              dec     ecx
00434b28 75f4            jne     awp+0x34b1e (00434b1e)

Crash Information

eax=0b28dc10 ebx=0b2183d0 ecx=0000011e edx=0b295258 esi=0b295258 edi=0000011e
eip=00434b20 esp=0018e490 ebp=0018e594 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
awp+0x34b20:
00434b20 ff448584        inc     dword ptr [ebp+eax*4-7Ch] ss:002b:2cbc5558=????????

0:000> u .
awp+0x34b20:
00434b20 ff448584        inc     dword ptr [ebp+eax*4-7Ch]
00434b24 83c204          add     edx,4
00434b27 49              dec     ecx
00434b28 75f4            jne     awp+0x34b1e (00434b1e)

0:000> ? poi(@ebp-c)
Evaluate expression: 286 = 0000011e

0:000> dc poi(@ebp-4)
0b295258  0b28dc10 00002da8 00000006 00000005  ..(..-..........
0b295268  00000005 00000006 00000008 00000005  ................
0b295278  00000006 00000006 00000006 00000005  ................
0b295288  00000005 00000006 00000007 00000006  ................
0b295298  00000005 00000004 00000006 00000006  ................
0b2952a8  00000006 00000006 00000005 00000006  ................
0b2952b8  00000009 00000007 00000007 00000006  ................
0b2952c8  00000005 00000006 00000007 00000005  ................

0:000> dc @ebp-7c
0018e518  00000000 00000000 00000000 00000000  ................
0018e528  00000000 00000000 00000000 00000000  ................
0018e538  00000000 00000000 00000000 00000000  ................
0018e548  00000000 00000000 00000000 00000000  ................

Exploit Proof of Concept

To use the proof of concept, simply open up or preview the document in the target application. The application should crash at the address specified due to memory corruption.

Timeline

2018-11-16 - Vendor Disclosure
2018-11-20 - Vendor Patched; Public Release

Credit

Discovered by a member of Cisco Talos.