CVE-2018-3998
An exploitable heap-based buffer overflow vulnerability exists in the Windows enhanced metafile parser of Atlantis Word Processor, version 3.2.5.0. A specially crafted image embedded within a document can cause an undersized allocation, resulting in an overflow when the application tries to copy data into it. An attacker must convince a victim to open a document in order to trigger this vulnerability.
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
https://www.atlantiswordprocessor.com/en/
8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CWE-122: Heap-based Buffer Overflow
Atlantis’ Word Processor is a traditional word processor that is aimed to be both portable, flexible, and full of a number of features. This word processor is ideally suited for 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 several similarities. Atlantis also has the capability 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 processing a Microsoft Word XML Document, the application has the ability to embed various images within the document. There are a number of image types, and these are handled by a class named TDocPicture
. When constructing a TDocPicture
class, the following function will be executed. This function will first check the header of the file to see if it matches a known signature for the PNG, GIF, or JFIF image formats. If the signature is not detected, then the application will determine the actual image type by checking the file extension embedded within the document against a global list. This enumeration is then passed to the function call at [1].
awp+0x1e6820:
005e6820 55 push ebp
005e6821 8bec mov ebp,esp
005e6823 83c4d0 add esp,0FFFFFFD0h
005e6826 53 push ebx
005e6827 56 push esi
005e6828 57 push edi
...
/* check the fingerprint of a few image file format types */
...
awp+0x1e6941:
005e6941 55 push ebp
005e6942 8bd3 mov edx,ebx // Image type enumeration
005e6944 8bc6 mov eax,esi
005e6946 e80dfeffff call awp+0x1e6758 (005e6758) // [1]
005e694b 59 pop ecx
005e694c 84c0 test al,al
005e694e 7438 je awp+0x1e6988 (005e6988)
Inside the function call at 0x5e6758
, the application will assign the image type enumeration to a local variable at [2]. Later at [3], the application will pass the image type enumeration as an argument to a function call. This function is responsible for constructing an instance of the class specific to the detected image file format. At [4], the application will enter a case statement and will then branch to the case that instantiates the constructor for the image format type specified by the enumeration.
awp+0x1e6758:
005e6758 55 push ebp
005e6759 8bec mov ebp,esp
005e675b 51 push ecx
005e675c 53 push ebx
005e675d 56 push esi
005e675e 8855ff mov byte ptr [ebp-1],dl // [2] Image type enumeration
005e6761 8bf0 mov esi,eax
...
awp+0x1e67e2:
005e67e2 55 push ebp
005e67e3 8a55ff mov dl,byte ptr [ebp-1] // Image type enumeration
005e67e6 8bc6 mov eax,esi
005e67e8 e88bfaffff call awp+0x1e6278 (005e6278) // [3] \
005e67ed 59 pop ecx
005e67ee 8845fe mov byte ptr [ebp-2],al
005e67f1 807dfe00 cmp byte ptr [ebp-2],0
005e67f5 7521 jne awp+0x1e6818 (005e6818)
\
awp+0x1e6278:
005e6278 55 push ebp
005e6279 8bec mov ebp,esp
005e627b 83c4e4 add esp,0FFFFFFE4h
005e627e 53 push ebx
005e627f 56 push esi
005e6280 57 push edi
005e6281 33c9 xor ecx,ecx
005e6283 894de8 mov dword ptr [ebp-18h],ecx
...
awp+0x1e62a4:
005e62a4 33c0 xor eax,eax
005e62a6 8ac2 mov al,dl // [4]
005e62a8 83f809 cmp eax,9
005e62ab 0f879c030000 ja awp+0x1e664d (005e664d)
005e62b1 ff2485b8625e00 jmp dword ptr awp+0x1e62b8 (005e62b8)[eax*4]
005e62b8 e062 loopne awp+0x1e631c (005e631c)
When handling a Windows metafile, it will branch to the following case. This handler will first construct an instance of a TMetaFile
object at [5] and then store it to a variable. A method will be called at [6] immediately afterward with the path to the actual image file. This method is a wrapper that will instantiate a TFileStream
object and then proceed to use the object to load the image file for usage by the TDocPicture
object.
awp+0x1e63e1:
005e63e1 b201 mov dl,1
005e63e3 a160fb4000 mov eax,dword ptr [awp+0xfb60 (0040fb60)] // TMetaFile
005e63e8 e807bde2ff call awp+0x120f4 (004120f4) // [5]
005e63ed 8945f0 mov dword ptr [ebp-10h],eax // variable containing TMetaFile instance
005e63f0 33c0 xor eax,eax
...
005e63f2 55 push ebp
005e63f3 6896645e00 push offset awp+0x1e6496 (005e6496)
005e63f8 64ff30 push dword ptr fs:[eax]
005e63fb 648920 mov dword ptr fs:[eax],esp
005e63fe 8b4508 mov eax,dword ptr [ebp+8] // frame
005e6401 8b4008 mov eax,dword ptr [eax+8] // caller frame
005e6404 8b50fc mov edx,dword ptr [eax-4] // path to image file
005e6407 8b45f0 mov eax,dword ptr [ebp-10h] // variable containing TMetaFile instance
005e640a 8b08 mov ecx,dword ptr [eax]
005e640c ff5138 call dword ptr [ecx+38h] // [6]
Inside this method, the following code will be executed to wrap the loading of the image. The method will first construct a TFileStream
object at [7] and then pass it as an argument to a method specific to each file format parser. The object’s method is called at [8] and is actually responsible for reading the image file contents.
awp+0x1187c:
0041187c 55 push ebp
0041187d 8bec mov ebp,esp
0041187f 51 push ecx
00411880 53 push ebx
00411881 8bd8 mov ebx,eax
00411883 6a20 push 20h
00411885 8bca mov ecx,edx
00411887 a1b86b4000 mov eax,dword ptr [awp+0x6bb8 (00406bb8)] // TFileStream
0041188c b201 mov dl,1
0041188e e8b97dffff call awp+0x964c (0040964c) // [7]
00411893 8945fc mov dword ptr [ebp-4],eax // TFileStream variable
...
awp+0x118a4:
004118a4 8b55fc mov edx,dword ptr [ebp-4]
004118a7 8bc3 mov eax,ebx
004118a9 8b08 mov ecx,dword ptr [eax]
004118ab ff5140 call dword ptr [ecx+40h] // [8] TMetaFile::LoadFromStream
The method that gets called is named TImageType::LoadFromStream
depending on the image type. This results in the following code being executed. This code is used to load data for parsing a Windows Enhanced Metafile image and starts out by calling the function at [9]. This call is responsible for verifying that the signature at the beginning of the file begins with “EMF” and aborting if not. Once the signature is verified, the application will finally make the call at [10] which will actually parse the image file header and contains the vulnerability within.
awp+0x12404:
00412404 53 push ebx
00412405 56 push esi
00412406 8bf2 mov esi,edx
00412408 8bd8 mov ebx,eax
0041240a 8bd6 mov edx,esi
0041240c 8bc3 mov eax,ebx
0041240e e8e1040000 call awp+0x128f4 (004128f4) // [9] TMetaFile::TestEMF
00412413 84c0 test al,al
00412415 740b je awp+0x12422 (00412422)
00412417 8bd6 mov edx,esi
00412419 8bc3 mov eax,ebx
0041241b e8cc000000 call awp+0x124ec (004124ec) // [10] Parse EMF file
00412420 eb1b jmp awp+0x1243d (0041243d)
The following function actually contains our vulnerability and starts by allocating 0x64 bytes on the stack. This space is used by the function to read the beginning of the EMF header. At [11], the application will call a function that wraps TFileStream::ReadFile
to accomplish this and load data from the file onto the stack. The header for an EMF file includes a field representing the size of the entire file. This field is used by the application at [12] to perform an allocation. Due to the size from the file’s header being provided by a user. This allows one to control the size of the allocation. Immediately following this allocation, the application will make a call to bcopy
at [13] using a hard-coded length of 0x64. Due to the file data being explicitly trusted to control the size used for the allocation of the destination pointer. An aggressor can specify a size less than 0x64, which will result in the call at [13] overflowing the destination buffer, which can allow for heap corruption. If the call to bcopy
for the 0x64 byte header does not corrupt memory, the following call at [14] that reads the rest of the file may corrupt memory. These types of heap corruptions can allow for code execution under the context of the application.
awp+0x124ec:
004124ec 53 push ebx
004124ed 56 push esi
004124ee 57 push edi
004124ef 55 push ebp
004124f0 83c49c add esp,0FFFFFF9Ch // allocate 0x64 bytes
...
004124fe 8bd4 mov edx,esp // destination
00412500 b964000000 mov ecx,64h // size
00412505 8bc5 mov eax,ebp
00412507 e8f86fffff call awp+0x9504 (00409504) // [11] calls TFileStream::ReadFile
0041250c 817c242820454d46 cmp dword ptr [esp+28h],464D4520h // verify signature once again
00412514 7405 je awp+0x1251b (0041251b)
...
0041251b 8b442430 mov eax,dword ptr [esp+30h] // size of metafile in header
0041251f e81cfefeff call awp+0x2340 (00402340) // [12] allocate space based on header
00412524 8bf0 mov esi,eax
...
00412526 8b5f10 mov ebx,dword ptr [edi+10h]
00412529 8bd6 mov edx,esi // pointer from previous allocation
0041252b 8bc4 mov eax,esp // header on stack
0041252d b964000000 mov ecx,64h // hardcoded size
00412532 e8a5fefeff call awp+0x23dc (004023dc) // [13] calls bcopy
...
00412537 8b4c2430 mov ecx,dword ptr [esp+30h] // Take the total file size
0041253b 83e964 sub ecx,64h // Subtract 0x64 for the header that was previously read
0041253e 8d5664 lea edx,[esi+64h] // Seek after the file header
00412541 8bc5 mov eax,ebp
00412543 e8bc6fffff call awp+0x9504 (00409504) // [14] Read the rest of the file into the heap buffer
(430.720): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000064 ebx=0b1f1490 ecx=00000014 edx=00000000 esi=0018ea14 edi=0b37ffec
eip=004023ed esp=0018ea08 ebp=0963ac2c iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
awp+0x23ed:
004023ed f3a5 rep movs dword ptr es:[edi],dword ptr [esi]
0:000> kv L4
# ChildEBP RetAddr Args to Child
00 0018ea0c 00412537 00000001 0000006c 00000000 awp+0x23ed
01 00000000 00000000 00000000 00000000 00000000 awp+0x12537
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.