Headline
CVE-2018-4000: TALOS-2018-0668 || Cisco Talos Intelligence Group
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.
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
Discovered by a member of Cisco Talos.