Talos Vulnerability Report

TALOS-2018-0668

Atlantis Word Processor Office Open XML TTableRow double free code execution vulnerability

October 1, 2018
CVE Number

CVE-2018-4000

Summary

An exploitable double-free vulnerability exists in the Office Open XML parser of Atlantis Word Processor, version 3.2.5.0. A specially crafted document can cause a TTableRow instance to be referenced twice, resulting in a double-free vulnerability when both the references go out of scope. An attacker must convince a victim to open a document in order to trigger this vulnerability.

Tested Versions

Atlantis Word Processor 3.2.5.0

start    end        module name
00400000 007f0000   awp      C (no symbols)           
    Loaded symbol image file: awp.exe
    Image path: C:\Program Files (x86)\Atlantis\awp.exe
    Image name: awp.exe
    File version:     3.2.5.0
    Product version:  3.2.5.0

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-415: Double Free

Details

Atlantis' Word Processor is a traditional word processor that is aimed to be both portable and flexible and contains a variety of features. This word processor is ideally suited for both writers and students and provides a number of useful features that can help simplify and even improve one's writing. Atlantis Word Processor is fully compatible with other word processors such as Microsoft Office Word 2007 and even has a similar interface. Atlantis also has the capability to encrypt document files and to fully customize the interface. This application is written in Delphi and contains the majority of its capabilities within a single relocatable binary.

When opening a document file, the application will construct an instance of the TDoc object. This instance will eventually be passed as an argument to the function containing the following code. This code is a case statement and is responsible for switching to the correct document parser based on the document file type enumeration that is stored within the TDoc instance. Eventually at [1], the application will call the function responsible for parsing Office Open XML documents.

awp+0x1ade4d:
005ade4d 8b45e8          mov     eax,dword ptr [ebp-18h]    // TDoc instance
005ade50 8b80dc000000    mov     eax,dword ptr [eax+0DCh]   // TDoc file type enumeration
005ade56 83f805          cmp     eax,5
005ade59 776a            ja      awp+0x1adec5 (005adec5)
005ade5b ff248562de5a00  jmp     dword ptr awp+0x1ade62 (005ade62)[eax*4]
...
005ade98 55              push    ebp
005ade99 e8ea27ffff      call    awp+0x1a0688 (005a0688)    // [1] call .docx file format parser
005ade9e 59              pop     ecx
005ade9f 8885d7f8ffff    mov     byte ptr [ebp-729h],al
005adea5 eb2b            jmp     awp+0x1aded2 (005aded2)

This function will initialize a number of variables that are used by the entire Office Open XML parser. This function is important in that it is considered by the author to be the upper-most function and all child functions called by this are executing within closures which allows numerous callees to modify the variables within this function. Some of these variables include the current styles that have been parsed, a variable containing the root TXML element, and even information about the table row (and table) that is currently being parsed. The current table row that this vulnerability revolves around is used specifically for keeping track of the current table row and is hence represented by a TTableRow object. This variable is located in this function's frame at %ebp-dc. After initializing some large arrays that are assumed by the author to contain different styles used by elements within the document, at [2] the application will search through the XML document for the "w:body" element. Once this is located, it will be passed as an argument to the function call at [3] in order to parse all of its child elements.

awp+0x1a0688:
005a0688 55              push    ebp
005a0689 8bec            mov     ebp,esp
005a068b 81c400ffffff    add     esp,0FFFFFF00h
005a0691 53              push    ebx
005a0692 56              push    esi
005a0693 57              push    edi
...
005a09f8 8b45b0          mov     eax,dword ptr [ebp-50h]                // TXML instance containing the entirety of the document.
005a09fb 8945f0          mov     dword ptr [ebp-10h],eax                // Instance is duplicated to this variable
005a09fe 55              push    ebp
005a09ff ba8c0c5a00      mov     edx,offset awp+0x1a0c8c (005a0c8c)     // Reference to "w:body" string
005a0a04 8b45b0          mov     eax,dword ptr [ebp-50h]
005a0a07 e80875f2ff      call    awp+0xc7f14 (004c7f14)                 // [2] Locate "w:body" element
...
005a0a0c 8a1570e86600    mov     dl,byte ptr [awp+0x26e870 (0066e870)]
005a0a12 e8e9fbffff      call    awp+0x1a0600 (005a0600)                // [3] Iterate through children of element
005a0a17 59              pop     ecx

As mentioned previously, the following function is used to iterate through all the children within the "w:body" tag. At the beginning of the function, this element is stored in the %edi register. A TXML element is also similar to a TList. At [4], the application will read the number of child elements from the TXML element and use it in the loop that follows. For each child element, the application will call the recursive function at [5].

awp+0x1a0600:
005a0600 55              push    ebp
005a0601 8bec            mov     ebp,esp
005a0603 81c4e0feffff    add     esp,0FFFFFEE0h
005a0609 53              push    ebx
005a060a 56              push    esi
005a060b 57              push    edi
005a060c 8895e3feffff    mov     byte ptr [ebp-11Dh],dl
005a0612 8bf8            mov     edi,eax                // "w:body" element
...
005a0647 8b7704          mov     esi,dword ptr [edi+4]  // [4] Grab the number of child elements
005a064a 4e              dec     esi
005a064b 85f6            test    esi,esi
005a064d 7c32            jl      awp+0x1a0681 (005a0681)
...
awp+0x1a0652:
005a0652 55              push    ebp
005a0653 8bd3            mov     edx,ebx
005a0655 8bc7            mov     eax,edi
005a0657 e89876e6ff      call    awp+0x7cf4 (00407cf4)      // Grab child from current element
005a065c e867d9ffff      call    awp+0x19dfc8 (0059dfc8)    // [5]
005a0661 59              pop     ecx
005a0662 80bde3feffff00  cmp     byte ptr [ebp-11Dh],0
005a0669 7412            je      awp+0x1a067d (005a067d)
...
005a067d 43              inc     ebx                        // Iterate to next child element
005a067e 4e              dec     esi
005a067f 75d1            jne     awp+0x1a0652 (005a0652)

This next function is a recursive function that is entirely responsible for parsing the XML that composes an Office Open XML document. As a result, it has a large number of cases used to dispatch to the correct parser for each specific element. This function is responsible for containing the scope of the TTableRow that is abused by this vulnerability. At [6], the function will first convert the XML tag name into a token/enumeration. This will then be used in a case statement in order to handle processing of the element. At [7], the TTableRow element will have one of its properties checked which will be used to determine whether this element needs to be saved. Finally, before the function leaves, the TTableRow element will be freed at [8].

awp+0x19dfc8:
0059dfc8 55              push    ebp
0059dfc9 8bec            mov     ebp,esp
0059dfcb 81c440ffffff    add     esp,0FFFFFF40h
0059dfd1 53              push    ebx
0059dfd2 56              push    esi
0059dfd3 57              push    edi
...
0059dfff 8b45fc          mov     eax,dword ptr [ebp-4]      // Current XML node
0059e002 8b4020          mov     eax,dword ptr [eax+20h]    // XML Tag name
0059e005 e82ea2ffff      call    awp+0x198238 (00598238)    // [6] Convert tag name to enumeration
0059e00a 83e07f          and     eax,7Fh
0059e00d 83f847          cmp     eax,47h
0059e010 7f3d            jg      awp+0x19e04f (0059e04f)    // Cases > 0x47
0059e012 0f845d0a0000    je      awp+0x19ea75 (0059ea75)
...
0059e04f 83c0b5          add     eax,0FFFFFFB5h             // Subtract 0x4b from enumeration
0059e052 83f812          cmp     eax,12h                    // Cases 0x4b through 0x5d
0059e055 0f874a220000    ja      awp+0x1a02a5 (005a02a5)
...
/* Cases for each specific XML tag in the document */
...
awp+0x1a0199:
005a0199 8b4508          mov     eax,dword ptr [ebp+8]      // Caller frame
005a019c 8b4008          mov     eax,dword ptr [eax+8]      // Frame containing TTableRow instance
005a019f 8b8024ffffff    mov     eax,dword ptr [eax-0DCh]   // TTableRow
005a01a5 83b8c800000000  cmp     dword ptr [eax+0C8h],0     // [7] Check property/style of TTableRow
005a01ac 0f8ec6000000    jle     awp+0x1a0278 (005a0278)
...
005a0278 8b4508          mov     eax,dword ptr [ebp+8]      // Caller frame
005a027b 8b4008          mov     eax,dword ptr [eax+8]      // Frame containing TTableRow instance
005a027e 8b8024ffffff    mov     eax,dword ptr [eax-0DCh]   // TTableRow
005a0284 e85f26e6ff      call    awp+0x28e8 (004028e8)      // [8] TObject::Free

When the previously defined function handles particular cases, the application makes some assumptions about the order in which these tags are processed. Unfortunately this can allow for scoping issues which results in the vulnerability described by this document. When handling case 0x5d which represents the "tr" (table row) tag, the following code is executed. First at [10], a new TTableRow object is constructed. Immediately following this at [11], the properties from the previously parsed row is copied into the current one. At this point at [12], the application will then proceed to search for any new properties that need to be applied to the current row. This is done by searching for a child element with the tag "tblPrEx" or "trPr". Once an element is found, one of the calls to [13] will extract the attributes from the element and apply them to the TTableRow object.

awp+0x1a001f:
005a001f 8b4508          mov     eax,dword ptr [ebp+8]
005a0022 83b8e4feffff01  cmp     dword ptr [eax-11Ch],1
005a0029 0f856d020000    jne     awp+0x1a029c (005a029c)
005a002f b201            mov     dl,1
005a0031 a19cee5500      mov     eax,dword ptr [awp+0x15ee9c (0055ee9c)]    // TTableRow
005a0036 e88528e6ff      call    awp+0x28c0 (004028c0)                      // [9] Construct a TTableRow instance
005a003b 8b5508          mov     edx,dword ptr [ebp+8]                      // Caller frame
005a003e 8b5208          mov     edx,dword ptr [edx+8]                      // Upper-most frame
005a0041 898224ffffff    mov     dword ptr [edx-0DCh],eax                   // [10] TTableRow that triggers vulnerability
...
005a0047 8b4508          mov     eax,dword ptr [ebp+8]                      // Caller frame
005a004a 8b4008          mov     eax,dword ptr [eax+8]                      // Upper-most frame
005a004d 8b8020ffffff    mov     eax,dword ptr [eax-0E0h]                   // Previous table row
005a0053 8b5508          mov     edx,dword ptr [ebp+8]                      // Caller frame
005a0056 8b5208          mov     edx,dword ptr [edx+8]                      // Upper-most frame
005a0059 8b9224ffffff    mov     edx,dword ptr [edx-0DCh]                   // TTableRow that triggers vulnerability
005a005f 8d7004          lea     esi,[eax+4]
005a0062 8d7a04          lea     edi,[edx+4]
005a0065 b914090000      mov     ecx,914h
005a006a f3a5            rep movs dword ptr es:[edi],dword ptr [esi]        // [11] Copy the properties from the previously parsed TTableRow
...
005a006c 8b4508          mov     eax,dword ptr [ebp+8]                      // Caller frame
005a006f 8b4008          mov     eax,dword ptr [eax+8]                      // Upper-most frame
005a0072 8b4084          mov     eax,dword ptr [eax-7Ch]                    // XML Style TList
005a0075 8b4004          mov     eax,dword ptr [eax+4]                      // XML Style TList length
005a0078 8945c0          mov     dword ptr [ebp-40h],eax                    // Backup the current style list length
...
005a007b 55              push    ebp
005a007c bae4055a00      mov     edx,offset awp+0x1a05e4 (005a05e4)         // "tblPrEx" tag
005a0081 8b45fc          mov     eax,dword ptr [ebp-4]
005a0084 e88b7ef2ff      call    awp+0xc7f14 (004c7f14)                     // [12] Search through children for table properties
...
005a0089 8b5508          mov     edx,dword ptr [ebp+8]
005a008c 8b5208          mov     edx,dword ptr [edx+8]
005a008f 8b9224ffffff    mov     edx,dword ptr [edx-0DCh]
005a0095 e8ead7ffff      call    awp+0x19d884 (0059d884)                    // [13] Handle properties defined in tag
005a009a 59              pop     ecx
...
005a00ac 55              push    ebp
005a00ad baf8055a00      mov     edx,offset awp+0x1a05f8 (005a05f8)         // "trPr" tag
005a00b2 8b45fc          mov     eax,dword ptr [ebp-4]
005a00b5 e85a7ef2ff      call    awp+0xc7f14 (004c7f14)                     // [13] Handle properties defined in tag
005a00ba e819deffff      call    awp+0x19ded8 (0059ded8)
005a00bf 59              pop     ecx

Later, when the application parses a table column identified by the "tc" tag in case 0x5b, the following code will be executed. Firstly, this case will grab an index out of the properties of the current table row and then use it to initialize a 0x90 byte object at [14]. After this, various styles will be extracted similar to the "tr" element (case 0x5d), described previously. However, the function call at [14] will then be called. This function call will recurse into the current function and iterate through the "tc" element's children. For future reference, this function call to 0x59cf1c is used to recurse into the various container elements by the document parser. When handling both the "pPr" tag (case 0x3c) and the "rPr" tag (case 0x49), the function at 0x59cf1c is called by [15].

awp+0x19fd95:
0059fd95 8b4508          mov     eax,dword ptr [ebp+8]
0059fd98 83b8e4feffff01  cmp     dword ptr [eax-11Ch],1
0059fd9f 0f856e020000    jne     awp+0x1a0013 (005a0013)
...
0059fda5 8b4508          mov     eax,dword ptr [ebp+8]      // Caller frame
0059fda8 8b4008          mov     eax,dword ptr [eax+8]      // Frame of upper-most function
0059fdab 8b8024ffffff    mov     eax,dword ptr [eax-0DCh]   // TTableRow used by this vulnerability
0059fdb1 8b80c8000000    mov     eax,dword ptr [eax+0C8h]   // Grab the index
0059fdb7 03c0            add     eax,eax
0059fdb9 8d04c0          lea     eax,[eax+eax*8]
0059fdbc 8b5508          mov     edx,dword ptr [ebp+8]      // Caller frame
0059fdbf 8b5208          mov     edx,dword ptr [edx+8]      // Frame of the upper-most function
0059fdc2 8b9224ffffff    mov     edx,dword ptr [edx-0DCh]   // TTableRow used by this vulnerability
0059fdc8 8d84c2cc000000  lea     eax,[edx+eax*8+0CCh]       // Offset of property
0059fdcf e8181efeff      call    awp+0x181bec (00581bec)    // [14] Initialize 0x90 byte structure
...
0059ff57 8b4508          mov     eax,dword ptr [ebp+8]      // Caller frame
0059ff5a 8b4008          mov     eax,dword ptr [eax+8]      // Frame of upper-most function
0059ff5d 8b4008          mov     eax,dword ptr [eax+8]      // Frame containing TDoc instance
0059ff60 8b40e8          mov     eax,dword ptr [eax-18h]    // TDoc instance
0059ff63 8b4050          mov     eax,dword ptr [eax+50h]    // TList of TPar elements
0059ff66 8b4004          mov     eax,dword ptr [eax+4]      // TList length
0059ff69 8945bc          mov     dword ptr [ebp-44h],eax    // Store paragraph list length
...
0059ff6c 55              push    ebp
0059ff6d e8aacfffff      call    awp+0x19cf1c (0059cf1c)    // [14] Recurse back into XML element parser
0059ff72 59              pop     ecx
...
awp+0x1a02a5:
005a02a5 55              push    ebp
005a02a6 e871ccffff      call    awp+0x19cf1c (0059cf1c)    // [15] Recurse back into XML element parser
005a02ab 59              pop     ecx
005a02ac 33c0            xor     eax,eax

When processing the malformed XML document included with this advisory in the proof-of-concept, one of the "rPr" elements is spread over two "tr" (TTableRow) elements. Due to the way this malformed document is parsed by the application, this results in the "rPr" element being constructed twice but pointing to the same parent "tr" (TTableRow) that the "rPr" tag is opened in. This results in the recursion parsing elements in the following approximate order. When parsing the "rPr" tag at [17], this object is constructed using the parent "tr" at [16]. Later when closing the tag, due to the "rPr" element being opened in the "tr" at [16], this results in the TObject::Free being called on the same "tr" element ([16]) twice. This results in a double-free vulnerability which can cause heap corruption. Proper manipulation of this heap corruption can lead to code execution under the context of the application.

<tr>                // [16] TTableRow that is double-referenced
    <tc>
        <pPr>
            <rPr>   // [17] Open tag belonging to current TTableRow

        </pPr>
    </tc>
<tr>                // Different TTableRow
    </rPr>          // [17] Close tag belonging to TTableRow at [16]

Crash Information

Set some breakpoints to monitor scope of TTableRow that is double-freed.

0:000> bp 5a0041 ".printf \"--> Constructed TTableRow and writing to -0xdc(%%ebp)\\n\""
0:000> bp 5a0284 ".printf \"--> Freeing TTableRow with TObject::Free\\n\""
0:007> g

Constructing the first TTableRow object.

--> Constructed TTableRow and writing to -0xdc(%ebp)
eax=0bba73ac ebx=00000001 ecx=5d5deed4 edx=0018f004 esi=00000001 edi=ffffffff
eip=005a0041 esp=0018ebcc ebp=0018eca4 iopl=0         nv up ei pl nz ac po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
awp+0x1a0041:
005a0041 898224ffffff    mov     dword ptr [edx-0DCh],eax ds:002b:0018ef28=24afb5e0

Pointer to first TTableRow object.

0:000> r eax
eax=0bba73ac

0:000> g

Constructing the second TTableRow object.

--> Constructed TTableRow and writing to -0xdc(%ebp)
eax=0bba9cb8 ebx=00000001 ecx=5d5deed4 edx=0018f004 esi=00000001 edi=0bba7388
eip=005a0041 esp=0018ead4 ebp=0018ebac iopl=0         nv up ei pl nz ac po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000212
awp+0x1a0041:
005a0041 898224ffffff    mov     dword ptr [edx-0DCh],eax ds:002b:0018ef28=0bba73ac

Pointer to second TTableRow object.

0:000> r eax
eax=0bba9cb8

Freeing the second TTableRow object.

--> Freeing TTableRow with TObject::Free
eax=0bba9cb8 ebx=ffffffff ecx=0018ebac edx=00000001 esi=0bba9cbc edi=0bba7388
eip=005a0284 esp=0018ead4 ebp=0018ebac iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
awp+0x1a0284:
005a0284 e85f26e6ff      call    awp+0x28e8 (004028e8)

Pointer to second TTableRow object.

0:000> r eax
eax=0bba9cb8

Freeing the second TTableRow object again.

0:000> g
--> Freeing TTableRow with TObject::Free
eax=0bba9cb8 ebx=ffffffff ecx=0018eca4 edx=00000001 esi=0bba73b0 edi=0bba7388
eip=005a0284 esp=0018ebcc ebp=0018eca4 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
awp+0x1a0284:
005a0284 e85f26e6ff      call    awp+0x28e8 (004028e8)

Freeing the second TTableRow object again.

0:000> r eax
eax=0bba9cb8

Heap is now corrupted

(38.978): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0bba9cb8 ebx=ffffffff ecx=0bb9a498 edx=00000001 esi=0bba73b0 edi=0bba7388
eip=00000000 esp=0018ebc4 ebp=0018eca4 iopl=0         nv up ei pl nz na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00010206
00000000 ??              ???

0:000> ub poi(@esp)
awp+0x28e0:
004028e0 e84b020000      call    awp+0x2b30 (00402b30)
004028e5 c3              ret
004028e6 8bc0            mov     eax,eax
004028e8 85c0            test    eax,eax
004028ea 7407            je      awp+0x28f3 (004028f3)
004028ec 8b08            mov     ecx,dword ptr [eax]
004028ee b201            mov     dl,1
004028f0 ff51fc          call    dword ptr [ecx-4]

Caller is pointing to TObject::Free

0:000> ub poi(@esp+4) L4
awp+0x1a0278:
005a0278 8b4508          mov     eax,dword ptr [ebp+8]
005a027b 8b4008          mov     eax,dword ptr [eax+8]
005a027e 8b8024ffffff    mov     eax,dword ptr [eax-0DCh]
005a0284 e85f26e6ff      call    awp+0x28e8 (004028e8)

Timeline

2018-09-10 - Vendor Disclosure
2018-09-11 - Vendor patched via beta version
2018-09-26 - Vendor released
2018-10-01 - Public Disclosure

Credit

Discovered by a member of Cisco Talos.