Talos Vulnerability Report

TALOS-2018-0667

Atlantis Word Processor JPEG length underflow code execution vulnerability

October 1, 2018
CVE Number

CVE-2018-3999

Summary

An exploitable stack-based buffer overflow vulnerability exists in the JPEG parser of Atlantis Word Processor, version 3.2.5.0. A specially crafted image embedded within a document can cause a length to be miscalculated and underflow. This length is then treated as unsigned and then used in a copying operation. Due to the length underflow, the application will then write outside the bounds of a stack buffer, resulting in a buffer overflow. 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-121: Stack-based Buffer Overflow

Details

Atlantis' Word Processor is a traditional word processor that is aimed to be both portable, flexible and contains a number 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 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 get 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. When checking the header, the application will will first check for a PNG signature via a call to @CompareMem [1]. The application will then check if the signature matches either a GIF or a PNG file. After checking for these signatures, the final check will look for the JFIF signature which is used to identify that the image is in JPEG format. Once identified as a JPEG, at [2] the application will assign an enumeration to a register and then pass it as an argument to [3].

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
...
005e68bf 8b15340e6700    mov     edx,dword ptr [awp+0x270e34 (00670e34)]    // PNG Signature (8 bytes)
005e68c5 8d45e0          lea     eax,[ebp-20h]
005e68c8 b908000000      mov     ecx,8
005e68cd e8ee4be2ff      call    awp+0xb4c0 (0040b4c0)                      // [1] @CompareMem
005e68d2 84c0            test    al,al
005e68d4 7404            je      awp+0x1e68da (005e68da)
...
005e68da 807de047        cmp     byte ptr [ebp-20h],47h                     // 'G'
005e68de 7510            jne     awp+0x1e68f0 (005e68f0)
005e68e0 807de149        cmp     byte ptr [ebp-1Fh],49h                     // 'I'
005e68e4 750a            jne     awp+0x1e68f0 (005e68f0)
005e68e6 807de246        cmp     byte ptr [ebp-1Eh],46h                     // 'F'
005e68ea 7504            jne     awp+0x1e68f0 (005e68f0)
005e68ec b304            mov     bl,4
005e68ee eb32            jmp     awp+0x1e6922 (005e6922)                    // Found GIF Signature
...
005e68f0 807de64a        cmp     byte ptr [ebp-1Ah],4Ah                     // 'J'
005e68f4 7512            jne     awp+0x1e6908 (005e6908)
005e68f6 807de746        cmp     byte ptr [ebp-19h],46h                     // 'F'
005e68fa 750c            jne     awp+0x1e6908 (005e6908)
005e68fc 807de849        cmp     byte ptr [ebp-18h],49h                     // 'I'
005e6900 7506            jne     awp+0x1e6908 (005e6908)
005e6902 807de946        cmp     byte ptr [ebp-17h],46h                     // 'F'
005e6906 7418            je      awp+0x1e6920 (005e6920)
...
005e6920 b303            mov     bl,3                                       // [2] Found JFIF Signature
005e6922 8b45dc          mov     eax,dword ptr [ebp-24h]
005e6925 e8bebfe1ff      call    awp+0x28e8 (004028e8)                      // TObject::Free
005e692a 33c0            xor     eax,eax
005e692c 8945dc          mov     dword ptr [ebp-24h],eax
...
005e6941 55              push    ebp
005e6942 8bd3            mov     edx,ebx                                    // Image type enumeration
005e6944 8bc6            mov     eax,esi
005e6946 e80dfeffff      call    awp+0x1e6758 (005e6758)                    // [3]
005e694b 59              pop     ecx
005e694c 84c0            test    al,al
005e694e 7438            je      awp+0x1e6988 (005e6988)

The application will assign the image type enumeration to a local variable at [4] inside the function call at 0x5e6758. Later at [5], 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 [6], 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        // [4] Image type enumeration
005e6761 8bf0            mov     esi,eax
...
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)    // [5] \
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
...
005e62a4 33c0            xor     eax,eax
005e62a6 8ac2            mov     al,dl                      // [6]
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)

The following case will be branched to when handling a JPEG. This handler will first construct an instance of a TJPEGImage object at [7] and then store it to a register. Immediately afterward, a method will be called at [8] with the path to the actual image file. This method is a wrapper which will instantiate a TFileStream object and then proceed to use the object to load the image file for usage by the TDocPicture object.

awp+0x1e649d:
005e649d b201            mov     dl,1
005e649f a150904100      mov     eax,dword ptr [awp+0x19050 (00419050)] // TJPEGImage
005e64a4 e8f72fe3ff      call    awp+0x194a0 (004194a0)                 // [7]
005e64a9 8bd8            mov     ebx,eax                                // register containing TJPEGImage instance
005e64ab 8b45e4          mov     eax,dword ptr [ebp-1Ch]
005e64ae 89584c          mov     dword ptr [eax+4Ch],ebx
005e64b1 33c0            xor     eax,eax
...
005e64b3 55              push    ebp
005e64b4 6810655e00      push    offset awp+0x1e6510 (005e6510)
005e64b9 64ff30          push    dword ptr fs:[eax]
005e64bc 648920          mov     dword ptr fs:[eax],esp
005e64bf 8b4508          mov     eax,dword ptr [ebp+8]                  // frame
005e64c2 8b4008          mov     eax,dword ptr [eax+8]                  // caller frame
005e64c5 8b50fc          mov     edx,dword ptr [eax-4]                  // path to image file
005e64c8 8bc3            mov     eax,ebx                                // register containing TJPEGImage instance
005e64ca 8b08            mov     ecx,dword ptr [eax]
005e64cc ff5138          call    dword ptr [ecx+38h]                    // [8]

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 [9] and then pass it as an argument to a method specific to each file format parser. The object's method is called at [10] 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)                  // [9]
00411893 8945fc          mov     dword ptr [ebp-4],eax                  // TFileStream variable
...
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]                    // [10] TJPEGImage::LoadFromStream

The function TJPEGImage::LoadFromStream is simply a wrapper around the lower-level JPEG parser. This function will call a couple of functions and then call the function responsible for initializing global JPEG parsing structures at [11]. Inside this function, a TJPEGData object will be constructed followed by constructing a TMemoryStream object. Eventually, the function call at [12] will be made. This function allocates space for a global structure that contains function pointers which will be used during JPEG decoding. Once this is complete, the call at [13] will be made, which will immediately make another function call at [14] which will actually process the markers inside a JPEG image.

awp+0x234b8:
004234b8 53              push    ebx
004234b9 56              push    esi
004234ba 8bda            mov     ebx,edx
004234bc 8bf0            mov     esi,eax
004234be 8bc3            mov     eax,ebx
004234c0 e80760feff      call    awp+0x94cc (004094cc)      // TStream::GetSize
004234c5 50              push    eax
004234c6 8bc3            mov     eax,ebx
004234c8 e8e35ffeff      call    awp+0x94b0 (004094b0)      // TStream::GetPosition
004234cd 5a              pop     edx
004234ce 2bd0            sub     edx,eax
004234d0 8bcb            mov     ecx,ebx
004234d2 8bc6            mov     eax,esi
004234d4 e873000000      call    awp+0x2354c (0042354c)     // [11] \
004234d9 5e              pop     esi
004234da 5b              pop     ebx
004234db c3              ret
\
awp+0x2354c:
0042354c 55              push    ebp
0042354d 8bec            mov     ebp,esp
0042354f 83c4f8          add     esp,0FFFFFFF8h
00423552 53              push    ebx
00423553 56              push    esi
00423554 57              push    edi
00423555 894df8          mov     dword ptr [ebp-8],ecx
00423558 8bf2            mov     esi,edx
0042355a 8945fc          mov     dword ptr [ebp-4],eax
...
00423598 e8bb74ffff      call    awp+0x1aa58 (0041aa58)     // [12] Allocate global structure containing function pointers
...
004235bd b001            mov     al,1
004235bf e87076ffff      call    awp+0x1ac34 (0041ac34)     // [13] \
\
awp+0x1ac34:
0041ac34 e887ffffff      call    awp+0x1abc0 (0041abc0)     // [14]
0041ac39 8bd0            mov     edx,eax

The JPEG parser utilized by the application retains the current JPEG marker used for decoding in a global. This is first checked at [15] to determine whether the current state is the "JPG" marker (FF C8). If it's not, then the application will continue executing and then do some arithmetic at [16] to see if the marker is "SOF9" (FF C9). The function will read the global structure containing the different function pointers that was described previously and then dispatch into the first one similar to to the call at [17] when handling each of these markers. This first function pointer will look at the current JPEG state and then finally call into a function at [18] to process each specific marker.

awp+0x1abc0:
0041abc0 53              push    ebx
0041abc1 813d64226700c8000000 cmp dword ptr [awp+0x272264 (00672264)],0C8h  // [15]
0041abcb 750f            jne     awp+0x1abdc (0041abdc)
...
0041abcd e8aefdffff      call    awp+0x1a980 (0041a980)                     // handle FFC8 marker
0041abd2 c70564226700c9000000 mov dword ptr [awp+0x272264 (00672264)],0C9h
...
0041abdc a164226700      mov     eax,dword ptr [awp+0x272264 (00672264)]    // check current marker
0041abe1 0538ffffff      add     eax,0FFFFFF38h                             // [16] -0xC8
0041abe6 83e802          sub     eax,2
0041abe9 720d            jb      awp+0x1abf8 (0041abf8)
...
0041abf8 8b1db0236700    mov     ebx,dword ptr [awp+0x2723b0 (006723b0)]    // global structure containing function pointers
0041abfe ff13            call    dword ptr [ebx]                            // [17] \
0041ac00 8bd8            mov     ebx,eax
\
awp+0x1a900:
0041a900 53              push    ebx
0041a901 56              push    esi
0041a902 8b1db0236700    mov     ebx,dword ptr [awp+0x2723b0 (006723b0)]    // global structure containing function pointers and jpeg state
0041a908 807b0d00        cmp     byte ptr [ebx+0Dh],0
0041a90c 7408            je      awp+0x1a916 (0041a916)
...
0041a916 e81df8ffff      call    awp+0x1a138 (0041a138)                     // [18] process specific markers
0041a91b 8bf0            mov     esi,eax

When processing specific markers, the following code will be executed. This code will perform a number of comparisons in order to narrow down on which function will be used to handle the different markers that can be encountered when parsing a JFIF stream, such as embedded within a JPEG image file format. At [19], application will use an index to dispatch into the handler for the currently parsed "APPx" marker.

awp+0x1a138:
0041a138 53              push    ebx
0041a139 56              push    esi
0041a13a bb5c226700      mov     ebx,offset awp+0x27225c (0067225c) // JFIF stream marker handlers
0041a13f 83bb4001000000  cmp     dword ptr [ebx+140h],0
0041a146 7517            jne     awp+0x1a15f (0041a15f)
...
0041a15f 8bb340010000    mov     esi,dword ptr [ebx+140h]
0041a165 8bc6            mov     eax,esi
0041a167 3dd8000000      cmp     eax,0D8h                           // "SOI" marker
0041a16c 7f61            jg      awp+0x1a1cf (0041a1cf)
...
0041a1cf 3ddc000000      cmp     eax,0DCh                           // "DNL" marker
0041a1d4 7f20            jg      awp+0x1a1f6 (0041a1f6)
...
0041a1f6 2ddd000000      sub     eax,0DDh                           // "DRI" marker
0041a1fb 0f848d000000    je      awp+0x1a28e (0041a28e)
0041a201 83c0fd          add     eax,0FFFFFFFDh
0041a204 83e810          sub     eax,10h
0041a207 0f8288000000    jb      awp+0x1a295 (0041a295)
...
0041a295 8b8358010000    mov     eax,dword ptr [ebx+158h]           // "APPx"  marker
0041a29b 8bb4b090fcffff  mov     esi,dword ptr [eax+esi*4-370h]
0041a2a2 ffd6            call    esi                                // [19]
0041a2a4 eb17            jmp     awp+0x1a2bd (0041a2bd)

This will then result in the following code being executed by the application to handle the different "APPx" marker types. The first 16 bits after an "APPx" marker will contain the length of the marker. At [20], the application will read the first eight bits, and then assign it to the high byte of a variable that will be referred to as the count. At [21], the next byte will be read and it will be added to the current value of the count variable. This composes the 16-bit word, which will be used as the length of the structure following the marker. At this point, the application will perform a signed comparison to see if the count is less than the value of zero. If it is less than or equal to zero, then the block at [22] will be executed which will explicitly set the value of count to zero.

awp+0x1a448:
0041a448 53              push    ebx
0041a449 56              push    esi
0041a44a 57              push    edi
0041a44b 55              push    ebp
0041a44c 83c4e4          add     esp,0FFFFFFE4h
0041a44f 8b2d68226700    mov     ebp,dword ptr [awp+0x272268 (00672268)]    // JPEG structure
0041a455 8b7d00          mov     edi,dword ptr [ebp]                        // JPEG file data
0041a458 8b7504          mov     esi,dword ptr [ebp+4]                      // JPEG leftover size
0041a45b 85f6            test    esi,esi
0041a45d 750b            jne     awp+0x1a46a (0041a46a)
...
0041a46a 4e              dec     esi
0041a46b 33c0            xor     eax,eax
0041a46d 8a07            mov     al,byte ptr [edi]                          // [20] read 8-bits
0041a46f c1e008          shl     eax,8                                      // shift it to the high byte
0041a472 890424          mov     dword ptr [esp],eax                        // count variable
0041a475 47              inc     edi
0041a476 85f6            test    esi,esi
0041a478 750b            jne     awp+0x1a485 (0041a485)
...
0041a485 4e              dec     esi
0041a486 33c0            xor     eax,eax
0041a488 8a07            mov     al,byte ptr [edi]                          // [21] read 8-bits
0041a48a 010424          add     dword ptr [esp],eax                        // binary OR it into the low byte of the count variable
0041a48d 47              inc     edi
0041a48e 832c2402        sub     dword ptr [esp],2                          // subtract 2 for length
0041a492 833c240e        cmp     dword ptr [esp],0Eh
0041a496 7c0a            jl      awp+0x1a4a2 (0041a4a2)                     // branch if less than 0xe
...
0041a4a2 833c2400        cmp     dword ptr [esp],0                          // check if count...
0041a4a6 7e09            jle     awp+0x1a4b1 (0041a4b1)                     // is <= 0
...
0041a4b1 33c0            xor     eax,eax                                    // [22] zero out the count
0041a4b3 89442404        mov     dword ptr [esp+4],eax

Finally, the application will use the count variable as a terminator in a loop. At [23], the application will decrease the count by one and then immediately increment it by one when entering the loop. If the 16-bit word following any of the "APPx" markers is zero, this will result in the following loop continuously iterating until the next time the count variable is zero. At [24], the application does a simple byte-for-byte copy by reading a byte from the %edi register, and writing it back to the %ebx register. The decrement for each iteration of the loop is done at [25]. Due to this loop being unbounded and writing into a local buffer, this can result in a stack-based buffer overflow which can lead to code execution under the context of the application. It is also prudent to note, that if this 16-bit length is larger than 0x20 then the stack-based buffer overflow will also occur due to the buffer having a maximum size of 0x20 bytes before overwriting the function's frame pointer.

awp+0x1a4b7:
0041a4b7 8b442404        mov     eax,dword ptr [esp+4]  // [23]
0041a4bb 48              dec     eax
0041a4bc 85c0            test    eax,eax
0041a4be 7225            jb      awp+0x1a4e5 (0041a4e5)
0041a4c0 40              inc     eax
0041a4c1 89442408        mov     dword ptr [esp+8],eax
...
0041a4c5 8d5c240c        lea     ebx,[esp+0Ch]          // destination buffer on the stack
...
awp+0x1a4c9:
0041a4c9 85f6            test    esi,esi
0041a4cb 750b            jne     awp+0x1a4d8 (0041a4d8)
0041a4cd e8e2f0ffff      call    awp+0x195b4 (004195b4)
0041a4d2 8b7d00          mov     edi,dword ptr [ebp]    // image data
0041a4d5 8b7504          mov     esi,dword ptr [ebp+4]  // leftover image size
0041a4d8 4e              dec     esi
...
0041a4d9 8a07            mov     al,byte ptr [edi]      // read a byte from the file
0041a4db 8803            mov     byte ptr [ebx],al      // [24] write into buffer
0041a4dd 47              inc     edi
0041a4de 43              inc     ebx
0041a4df ff4c2408        dec     dword ptr [esp+8]      // [25] decrease count variable
0041a4e3 75e4            jne     awp+0x1a4c9 (0041a4c9)

Crash Information

(568.7d4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000000 ebx=00190000 ecx=000000e0 edx=0b190008 esi=000022d0 edi=0b1915ee
eip=0041a4db esp=0018ea10 ebp=0b18b194 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+0x1a4db:
0041a4db 8803            mov     byte ptr [ebx],al          ds:002b:00190000=41

0:000> u . L5
awp+0x1a4db:
0041a4db 8803            mov     byte ptr [ebx],al
0041a4dd 47              inc     edi
0041a4de 43              inc     ebx
0041a4df ff4c2408        dec     dword ptr [esp+8]
0041a4e3 75e4            jne     awp+0x1a4c9 (0041a4c9)

0:000> ? dwo(@ebp+8)
Evaluate expression: 186060684 = 0b170f8c

0:000> ? @ebx-@esp
Evaluate expression: 5616 = 000015f0

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.