Vulnerabilities > CVE-2017-2791 - Improper Restriction of Operations within the Bounds of a Memory Buffer vulnerability in Justsystems Ichitaro 2016

Attack vector
Attack complexity
Privileges required
Confidentiality impact
Integrity impact
Availability impact


JustSystems Ichitaro 2016 Trial contains a vulnerability that exists when trying to open a specially crafted PowerPoint file. Due to the application incorrectly handling the error case for a function's result, the application will use this result in a pointer calculation for reading file data into. Due to this, the application will read data from the file into an invalid address thus corrupting memory. Under the right conditions, this can lead to code execution under the context of the application.

Vulnerable Configurations

Part Description Count

Common Attack Pattern Enumeration and Classification (CAPEC)

  • Buffer Overflow via Environment Variables
    This attack pattern involves causing a buffer overflow through manipulation of environment variables. Once the attacker finds that they can modify an environment variable, they may try to overflow associated buffers. This attack leverages implicit trust often placed in environment variables.
  • Overflow Buffers
    Buffer Overflow attacks target improper or missing bounds checking on buffer operations, typically triggered by input injected by an attacker. As a consequence, an attacker is able to write past the boundaries of allocated buffer regions in memory, causing a program crash or potentially redirection of execution as per the attackers' choice.
  • Client-side Injection-induced Buffer Overflow
    This type of attack exploits a buffer overflow vulnerability in targeted client software through injection of malicious content from a custom-built hostile service.
  • Filter Failure through Buffer Overflow
    In this attack, the idea is to cause an active filter to fail by causing an oversized transaction. An attacker may try to feed overly long input strings to the program in an attempt to overwhelm the filter (by causing a buffer overflow) and hoping that the filter does not fail securely (i.e. the user input is let into the system unfiltered).
  • MIME Conversion
    An attacker exploits a weakness in the MIME conversion routine to cause a buffer overflow and gain control over the mail server machine. The MIME system is designed to allow various different information formats to be interpreted and sent via e-mail. Attack points exist when data are converted to MIME compatible format and back.


description### Summary Ichitaro Office contains a vulnerability that exists when trying to open a specially crafted PowerPoint file. Due to the application incorrectly handling the error case for a function's result, the application will use this result in a pointer calculation for reading file data into. Due to this, the application will read data from the file into an invalid address thus corrupting memory. Under the right conditions this can lead to code execution under the context of the application. ### Tested Versions JustSystems Ichitaro 2016 Trial ``` 0:000> lm vm jxxtppt 06bc0000 06c47000 JXXTPPT C (export symbols) JXXTPPT.DLL Image path: C:\Program Files\JustSystems\JSLIB32\JXXTPPT.DLL File version: Product version: 0:000> lm vm jsvda 277a0000 27826000 jsvda (export symbols) jsvda.dll Image path: C:\Program Files\JustSystems\JSLIB32\jsvda.dll File version: 3.3.312.1 Product version: 3.3.312.1 ``` ### Product URLs ### CVSSv3 Score 7.5 - CVSS:3.0/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H ### Details Ichitaro's word-processor includes the ability to parse data from various Microsoft Office document formats. Each of these document formats are stored using Microsoft's Structured Storage format. When using these formats, the document contents are organized in the form of a list of streams. Within each stream is a list of records that are used to describe the attributes and contents of various parts of the document. When processing a Powerpoint Document (.ppt), the application will first have to identify the "PowerPoint Document" stream. Once the correct stream has been identified, the application will proceed to read records within the file in order to render its contents to the user. Each of these records are referred to as an Atom which is prefixed with a type and length and followed by its contents. In order to determine the records of the document that were last edited, the application will first look up the "Current User" stream. This stream is typically a single-record that describes where the PersistDirectory records are at within the "PowerPoint Document" stream. The application begins to do this at address 0x6bd64a1. Once identifying the correct streams and storing them at +0x28 and +0x2c of an object, the application will read 0x14 bytes for the structure "CurrentUserAtom" from the "Current User" stream. This is portrayed in the following code. ``` JXXTPPT!JXXTPPT_Jsfc_Convert+0x15176: 06bd64a1 68e808c306 push offset JXXTPPT!DRFL_SaveFD3A+0x32e6d (06c308e8) ; "Current User 06bd64a6 8b4590 mov eax,dword ptr [ebp-70h] ; *this 06bd64a9 83c028 add eax,28h 06bd64ac 50 push eax ; *this+0x28 06bd64ad 8b4d08 mov ecx,dword ptr [ebp+8] 06bd64b0 51 push ecx 06bd64b1 8b4d90 mov ecx,dword ptr [ebp-70h] 06bd64b4 e8b8010000 call JXXTPPT!JXXTPPT_Jsfc_Convert+0x15346 (06bd6671) 06bd64b9 85c0 test eax,eax 06bd64bb 7438 je JXXTPPT!JXXTPPT_Jsfc_Convert+0x151ca (06bd64f5) ... JXXTPPT!JXXTPPT_Jsfc_Convert+0x15259: 06bd6584 6a14 push 14h 06bd6586 8d4da0 lea ecx,[ebp-60h] 06bd6589 51 push ecx ; Target buffer of CurrentUser atom 06bd658a 8b5590 mov edx,dword ptr [ebp-70h] 06bd658d 8b4228 mov eax,dword ptr [edx+28h] ; "Current User" stream 06bd6590 50 push eax 06bd6591 e8deb5ffff call JXXTPPT!JXXTPPT_Jsfc_Convert+0x10849 (06bd1b74) ; Read 0x14 bytes from stream 06bd6596 83c40c add esp,0Ch ``` After the CurrentUser record's structure has been filled, the application will use one of its fields, OffsetToCurrentEdit, to determine where the actual UserEditAtom is located within the PowerPoint Document stream. the application does this by seeking into the stream at address 0x6bd65b7. Afterwards, the application will read 0x1c bytes for the UserEditAtom header, and then call into the function at 0x6bd6601. The function at 0x6bd7372 will construct an object for the PersistDirectory within the Powerpoint file and read the number of directory entries as described in the file format specification. ``` JXXTPPT!JXXTPPT_Jsfc_Convert+0x1527c: 06bd65a7 8b4da8 mov ecx,dword ptr [ebp-58h] ; CurrentUser.OffsetToCurrentEdit 06bd65aa 83c108 add ecx,8 06bd65ad 51 push ecx 06bd65ae 6a00 push 0 06bd65b0 8b5590 mov edx,dword ptr [ebp-70h] 06bd65b3 8b422c mov eax,dword ptr [edx+2Ch] ; "PowerPoint Document" stream 06bd65b6 50 push eax 06bd65b7 e887b5ffff call JXXTPPT!JXXTPPT_Jsfc_Convert+0x10818 (06bd1b43) ; Seek to offset of UserEditAtom 06bd65bc 83c40c add esp,0Ch ... JXXTPPT!JXXTPPT_Jsfc_Convert+0x152a2: 06bd65cd 6a1c push 1Ch 06bd65cf 8d4dd8 lea ecx,[ebp-28h] ; Target buffer of file data 06bd65d2 51 push ecx 06bd65d3 8b5590 mov edx,dword ptr [ebp-70h] 06bd65d6 8b422c mov eax,dword ptr [edx+2Ch] ; "PowerPoint Document" stream. 06bd65d9 50 push eax 06bd65da e895b5ffff call JXXTPPT!JXXTPPT_Jsfc_Convert+0x10849 (06bd1b74) ; Read 0x1c bytes of data from file 06bd65df 83c40c add esp,0Ch ... JXXTPPT!JXXTPPT_Jsfc_Convert+0x152c2: 06bd65ed b907000000 mov ecx,7 06bd65f2 8d75d8 lea esi,[ebp-28h] ; 0x1c bytes of data 06bd65f5 8d7db4 lea edi,[ebp-4Ch] 06bd65f8 f3a5 rep movs dword ptr es:[edi],dword ptr [esi] ; Copy to %ebp-4c 06bd65fa 8d4db4 lea ecx,[ebp-4Ch] ; Header data of UserEditAtom 06bd65fd 51 push ecx 06bd65fe 8b4d90 mov ecx,dword ptr [ebp-70h] 06bd6601 e86c0d0000 call JXXTPPT!JXXTPPT_Jsfc_Convert+0x16047 (06bd7372) ; Parse Record ``` Inside the function at 0x6bd7372, the application will allocate space for a 0x4c object and initialize it with a record-type of 0x1772. This record-type corresponds with the PersistDirectory record described within the Powerpoint specification. The constructor for this 0x4c object is also responsible for initializing the index that is overflown at offset 0x40 of the structure. This index is used to calculate the total number of offsets that are maintained within the PersistDirectory records for the document and is used to determine how much space to allocate for them. ``` JXXTPPT!JXXTPPT_Jsfc_Convert+0x16081: 06bd73ac 6a4c push 4Ch 06bd73ae e8dbbffeff call JXXTPPT!JXXTPPT_Jsfc_Convert+0x2063 (06bc338e) ; Allocate 0x4c bytes of space 06bd73b3 83c404 add esp,4 ... 06bd73c6 6a00 push 0 06bd73c8 8b4dbc mov ecx,dword ptr [ebp-44h] 06bd73cb e850970000 call JXXTPPT!JXXTPPT_Jsfc_Convert+0x1f7f5 (06be0b20) ; \ \ JXXTPPT!JXXTPPT_Jsfc_Convert+0x1f811: 06be0b3c 6872170000 push 1772h ; Enumeration for RT_PersistDirectoryAtom 06be0b41 8b4508 mov eax,dword ptr [ebp+8] 06be0b44 50 push eax 06be0b45 8b4df0 mov ecx,dword ptr [ebp-10h] 06be0b48 e8a387ffff call JXXTPPT!JXXTPPT_Jsfc_Convert+0x17fc5 (06bd92f0) ; atom record object ... 06be0b54 8b4df0 mov ecx,dword ptr [ebp-10h] 06be0b57 83c140 add ecx,40h ; Index at +0x40 06be0b5a e8c1e60000 call JXXTPPT!JXXTPPT_Jsfc_Convert+0x2def5 (06bef220) ; Construct an object ``` After constructing the 0x4c byte object, the application will return back to the function 0x6bd7372 at the address 0x6bd73d0. This function contains numerous loops in order to properly parse the UserEditAtom and all the PersistDirectory entries within a document. The outer-most loop will read the UserEditAtom, seek to the dword specified in the offsetPersistDirectory field, and then will simply consume all the offsets of the record within the PersistDirectoryAtoms. ``` JXXTPPT!JXXTPPT_Jsfc_Convert+0x16119: 06bd7444 6a04 push 4 06bd7446 8d55cc lea edx,[ebp-34h] 06bd7449 52 push edx 06bd744a 8b45ac mov eax,dword ptr [ebp-54h] 06bd744d 8b482c mov ecx,dword ptr [eax+2Ch] 06bd7450 51 push ecx 06bd7451 e81ea7ffff call JXXTPPT!JXXTPPT_Jsfc_Convert+0x10849 (06bd1b74) ; Read 4 bytes from the file for the offset 06bd7456 83c40c add esp,0Ch ... JXXTPPT!JXXTPPT_Jsfc_Convert+0x161b7: 06bd74e2 8b4ddc mov ecx,dword ptr [ebp-24h] ; cPersist field from record 06bd74e5 8b55e0 mov edx,dword ptr [ebp-20h] ; \ 06bd74e8 8d048a lea eax,[edx+ecx*4] ; |- Seek current position forward. 06bd74eb 8945e0 mov dword ptr [ebp-20h],eax ; / 06bd74ee 8b4de0 mov ecx,dword ptr [ebp-20h] ; current position 06bd74f1 3b4dd0 cmp ecx,dword ptr [ebp-30h] ; record size 06bd74f4 0f824affffff jb JXXTPPT!JXXTPPT_Jsfc_Convert+0x16119 (06bd7444) ``` For each iteration of this loop, the application will read a dword that determines the PersistId as well as the number of offsets that are stored as cPersist. Using cPersist as the number of offsets to read, the next loop will then read each offset and update the fields in the object at %ebp-10. This object is responsible for containing the PersistId as well as each offset for every single persist directory entry within the document. The field at 0x40 of this object will be incremented for each offset that is read out of all the records within the document. ``` JXXTPPT!JXXTPPT_Jsfc_Convert+0x16145: 06bd7470 8b45cc mov eax,dword ptr [ebp-34h] 06bd7473 c1e814 shr eax,14h 06bd7476 25ff0f0000 and eax,0FFFh 06bd747b 8945dc mov dword ptr [ebp-24h],eax ; PersistDirectoryEntry.cPersist 06bd747e 8b4dcc mov ecx,dword ptr [ebp-34h] 06bd7481 81e1ffff0f00 and ecx,0FFFFFh 06bd7487 894dc8 mov dword ptr [ebp-38h],ecx ; PersistDirectoryEntry.PersistId ... JXXTPPT!JXXTPPT_Jsfc_Convert+0x16168: ; loop 06bd7493 8b55c4 mov edx,dword ptr [ebp-3Ch] ; \ 06bd7496 83c201 add edx,1 ; |- cPersist Index 06bd7499 8955c4 mov dword ptr [ebp-3Ch],edx ; / 06bd749c 8b45c4 mov eax,dword ptr [ebp-3Ch] 06bd749f 3b45dc cmp eax,dword ptr [ebp-24h] ; Check if cPersist Index is larger than cPersist 06bd74a2 733e jae JXXTPPT!JXXTPPT_Jsfc_Convert+0x161b7 (06bd74e2) ... 06bd74a4 6a04 push 4 06bd74a6 8d4dd4 lea ecx,[ebp-2Ch] ; offset 06bd74a9 51 push ecx 06bd74aa 8b55ac mov edx,dword ptr [ebp-54h] 06bd74ad 8b422c mov eax,dword ptr [edx+2Ch] 06bd74b0 50 push eax 06bd74b1 e8bea6ffff call JXXTPPT!JXXTPPT_Jsfc_Convert+0x10849 (06bd1b74) ; Read 4 bytes for the offset 06bd74b6 83c40c add esp,0Ch ... JXXTPPT!JXXTPPT_Jsfc_Convert+0x1619c: 06bd74c7 8b4dd4 mov ecx,dword ptr [ebp-2Ch] ; offset 06bd74ca 51 push ecx 06bd74cb 8b55c8 mov edx,dword ptr [ebp-38h] ; persistId 06bd74ce 52 push edx 06bd74cf 8b4df0 mov ecx,dword ptr [ebp-10h] 06bd74d2 e857970000 call JXXTPPT!JXXTPPT_Jsfc_Convert+0x1f903 (06be0c2e) ; XXX: Store offset/persistId and increase current object+40 count 06bd74d7 8b45c8 mov eax,dword ptr [ebp-38h] ; \ 06bd74da 83c001 add eax,1 ; |- Increase persistId 06bd74dd 8945c8 mov dword ptr [ebp-38h],eax ; / 06bd74e0 ebb1 jmp JXXTPPT!JXXTPPT_Jsfc_Convert+0x16168 (06bd7493) ; continue loop ``` Immediately following this loop, the application will process the rest of the PersistDirectory records linked by the first record. It does this by checking to see if CurrentAtom.offsetLastEdit is 0. If this is true, the loop will terminate. Otherwise, the loop will seek to the specified offset of the document and continue to read offsets out of the record similarly to the first loop described earlier. ``` JXXTPPT!JXXTPPT_Jsfc_Convert+0x161cf: 06bd74fa 8b5508 mov edx,dword ptr [ebp+8] ; CurrentAtom 06bd74fd 837a0800 cmp dword ptr [edx+8],0 ; Break if CurrentAtom.offsetLastEdit is 0 06bd7501 0f8460010000 je JXXTPPT!JXXTPPT_Jsfc_Convert+0x1633c (06bd7667) ... JXXTPPT!JXXTPPT_Jsfc_Convert+0x1631f: 06bd764a 8b45dc mov eax,dword ptr [ebp-24h] ; cPersist 06bd764d 8b4de0 mov ecx,dword ptr [ebp-20h] ; \ 06bd7650 8d1481 lea edx,[ecx+eax*4] ; |- currentPsition 06bd7653 8955e0 mov dword ptr [ebp-20h],edx ; / 06bd7656 8b45e0 mov eax,dword ptr [ebp-20h] ; currentPosition 06bd7659 3b45d0 cmp eax,dword ptr [ebp-30h] ; check if currentPosition is larger than record size 06bd765c 0f824dffffff jb JXXTPPT!JXXTPPT_Jsfc_Convert+0x16284 (06bd75af) 06bd7662 e993feffff jmp JXXTPPT!JXXTPPT_Jsfc_Convert+0x161cf (06bd74fa) ``` Inside this loop, the application will seek to the file offset specified in the current record's OffsetLastEdit field. The root of this vulnerability is due to an aggressor being allowed to specify an OffsetLastEdit field that points to any number of chained records. This allows for an attacker to be able to control when this loop terminates and can set the number of offsets to any value they deem useful. This loop has a similar functionality of reading offsets based on the value of the cPersist and PersistId fields that was described prior. This can be used to corrupt arbitrary memory which will be described later. ``` JXXTPPT!JXXTPPT_Jsfc_Convert+0x161e3: 06bd750e 8b4508 mov eax,dword ptr [ebp+8] ; Record Header 06bd7511 8b4808 mov ecx,dword ptr [eax+8] ; offsetLastEdit 06bd7514 83c108 add ecx,8 ; seek past record's header 06bd7517 51 push ecx 06bd7518 6a00 push 0 06bd751a 8b55ac mov edx,dword ptr [ebp-54h] 06bd751d 8b422c mov eax,dword ptr [edx+2Ch] ; file object 06bd7520 50 push eax 06bd7521 e81da6ffff call JXXTPPT!JXXTPPT_Jsfc_Convert+0x10818 (06bd1b43) ; seek the file pointer to offsetLastEdit+8 06bd7526 83c40c add esp,0Ch ``` Before entering the inner-most loop for reading offsets, however, the application will determine the PersistId and the number of offsets that follow by reading a dword and extracting a number from it's bits. These will be used to read the number of offsets from the document and store them for retrieval later. The function call at 0x6bd763a will be used to store the offset and PersistId for each iteration of the loop. ``` JXXTPPT!JXXTPPT_Jsfc_Convert+0x162b0: 06bd75db 8b55cc mov edx,dword ptr [ebp-34h] 06bd75de c1ea14 shr edx,14h 06bd75e1 81e2ff0f0000 and edx,0FFFh 06bd75e7 8955dc mov dword ptr [ebp-24h],edx ; cPersist 06bd75ea 8b45cc mov eax,dword ptr [ebp-34h] 06bd75ed 25ffff0f00 and eax,0FFFFFh 06bd75f2 8945c8 mov dword ptr [ebp-38h],eax ; PersistId ... JXXTPPT!JXXTPPT_Jsfc_Convert+0x162d3: ; loop 06bd75fe 8b4dc0 mov ecx,dword ptr [ebp-40h] ; \ 06bd7601 83c101 add ecx,1 ; |- increase current index 06bd7604 894dc0 mov dword ptr [ebp-40h],ecx ; / 06bd7607 8b55c0 mov edx,dword ptr [ebp-40h] ; current index 06bd760a 3b55dc cmp edx,dword ptr [ebp-24h] ; check if index larger than cPersist 06bd760d 733b jae JXXTPPT!JXXTPPT_Jsfc_Convert+0x1631f (06bd764a) ... JXXTPPT!JXXTPPT_Jsfc_Convert+0x16304: 06bd762f 8b45d4 mov eax,dword ptr [ebp-2Ch] ; offset that was read 06bd7632 50 push eax 06bd7633 8b4dc8 mov ecx,dword ptr [ebp-38h] ; PersistId 06bd7636 51 push ecx 06bd7637 8b4df0 mov ecx,dword ptr [ebp-10h] ; file object 06bd763a e8ef950000 call JXXTPPT!JXXTPPT_Jsfc_Convert+0x1f903 (06be0c2e) ; XXX: Increases Current Offset Index 06bd763f 8b55c8 mov edx,dword ptr [ebp-38h] ; \ 06bd7642 83c201 add edx,1 ; |- PersistId 06bd7645 8955c8 mov dword ptr [ebp-38h],edx ; / 06bd7648 ebb4 jmp JXXTPPT!JXXTPPT_Jsfc_Convert+0x162d3 (06bd75fe) ; continue loop ``` At the function 0x6be0c2e, the application will allocate 8 bytes of space to store the PersistId and the offset. Also within this function, the aplication will update the current index of offsets. This is done to offset 0x40 of the PersistDirectory object that was allocated with the enumeration 0x1772 earlier. This counter represents the number of offsets that have been read and will be increased without any constraints on it's bounds. Due to a potential attacker being able to control when the linked list of Persist Records terminates as well as the number of offsets that are within a record allows one to set this field to any value that they choose. ``` JXXTPPT!JXXTPPT_Jsfc_Convert+0x1f90c: 06be0c37 6a08 push 8 06be0c39 e85027feff call JXXTPPT!JXXTPPT_Jsfc_Convert+0x2063 (06bc338e) ; allocate space 06be0c3e 83c404 add esp,4 ... JXXTPPT!JXXTPPT_Jsfc_Convert+0x1f92a: 06be0c55 8b4dfc mov ecx,dword ptr [ebp-4] ; pair 06be0c58 51 push ecx 06be0c59 8b4df8 mov ecx,dword ptr [ebp-8] ; 0x1772 Tagged Object 06be0c5c 83c140 add ecx,40h ; XXX: Points to the current number of offsets that have been read 06be0c5f e83c7bffff call JXXTPPT!JXXTPPT_Jsfc_Convert+0x17475 (06bd87a0) ; Update Index in Tagged Object ``` The function at 0x6bd87a0 is simply a wrapper that calls 0x6be5ba0. Inside the function at 0x6be5ba0, the application will check to see if there's enough space for the current number of offsets. If this is not the case then the application will increase the current size by a power of two and then resize it using realloc. It is at address 0x6be5bf6 that the application immediately stores this to *this+8 without checking to see if realloc has returned a failure result (NULL). Due to this oversight, the function call can fail under memory pressure which causes some pointer arithmetic that follows to point outside the bounds of the original allocation. ``` JXXTPPT!JXXTPPT_Jsfc_Convert+0x1747c: 06bd87a7 8b45fc mov eax,dword ptr [ebp-4] ; XXX: Number of offsets that have been read 06bd87aa 8b08 mov ecx,dword ptr [eax] 06bd87ac 51 push ecx 06bd87ad 8b5508 mov edx,dword ptr [ebp+8] ; Pair 06bd87b0 52 push edx 06bd87b1 8b4dfc mov ecx,dword ptr [ebp-4] ; XXX: Number of offsets that have been read 06bd87b4 e8e7d30000 call JXXTPPT!JXXTPPT_Jsfc_Convert+0x24875 (06be5ba0) \ \ JXXTPPT!JXXTPPT_Jsfc_Convert+0x248a6: 06be5bd1 8b55f8 mov edx,dword ptr [ebp-8] ; Take original size 06be5bd4 d1e2 shl edx,1 ; Increase size by power of two 06be5bd6 8b45fc mov eax,dword ptr [ebp-4] ; 06be5bd9 895004 mov dword ptr [eax+4],edx ; Store it back to object 06be5bdc 8b4dfc mov ecx,dword ptr [ebp-4] 06be5bdf 8b5104 mov edx,dword ptr [ecx+4] ; Read that size back 06be5be2 c1e202 shl edx,2 06be5be5 52 push edx ; new size 06be5be6 8b45fc mov eax,dword ptr [ebp-4] 06be5be9 8b4808 mov ecx,dword ptr [eax+8] 06be5bec 51 push ecx ; old pointer 06be5bed ff15f424c206 call dword ptr [JXXTPPT!DRFL_SaveFD3A+0x24a79 (06c224f4)] ; calls realloc 06be5bf3 83c408 add esp,8 06be5bf6 8b55fc mov edx,dword ptr [ebp-4] 06be5bf9 894208 mov dword ptr [edx+8],eax ; XXX: Store pointer without checking for failure ``` Immediately after resizing the buffer to the next power of two, the application will proceed to write the user-controlled offset to NULL + Index*4. This actually happens at address 0x6be5c38. Normally a near-NULL write is not exploitable on modern systems due to NULL page constraints, but due to an attacker nearly being able to control the index that's being added to this pointer, one can specify an index that's larger than a page. In this situation, utilizing a technique such as a heapspray to force a useful data structure being mapped at an address that's a power of two, an aggressor could potentially corrupt memory that might allow them to manipulate more of the state of the application. ``` JXXTPPT!JXXTPPT_Jsfc_Convert+0x24901: 06be5c2c 8b4dfc mov ecx,dword ptr [ebp-4] 06be5c2f 8b5108 mov edx,dword ptr [ecx+8] ; NULL pointer from re-alloc 06be5c32 8b450c mov eax,dword ptr [ebp+0Ch] ; Number of offsets currently read 06be5c35 8b4d08 mov ecx,dword ptr [ebp+8] ; Pair 06be5c38 890c82 mov dword ptr [edx+eax*4],ecx ; XXX: Write offset to user-controlled value ``` ### File-Format Details Ichitaro Word Processor is able to open various file formats that have been created by the Microsoft Office Suite of applications. These file formats are encoded in a filesystem of sorts known as the Structured Storage file-format. The Structured Storage file-format has the ability to encode multiple files/streams within the document. It is within these streams that the contents of the document can be located. Each of these streams contain a list of records that are each prefixed with the following header. Within this structure, a record contains a 4-bit version followed by a 12-bit Instance. Following it is a 16-bit type, a 32-bit length, followed by the record's contents. ``` <class Header> 'header' [86b7e] <instance VersionInstance 'Version/Instance'> 0 / 0x000 [86b80] <instance RecordType 'Type'> RT_UserEditAtom(0xff5) [86b82] <instance uint32_t 'Length'> 0x0000001c (28) ``` Despite the contents of a Powerpoint document being primarily contained within the "PowerPoint Document" stream, the application starts by reading the contents of the "Current User" stream. The Current User stream consists entirely of just a single record named the CurrentUserAtom. At offset 0 of the stream will be the following structure. Within this structure at offset 0x10 is a dword that represents the offset into the "PowerPoint Document" stream. This offset points to another structure named the UserEditAtom which contains information about the previous edits that were made to the document. The offset in the following structure is based it's location within the provided sample. ``` <class RecordGeneral> 'unnamed_547bea0' {unnamed=True} [57fcc0] <instance Header 'header'> version=0 instance=0x000 type=0x0ff6 length=0x0000001d [57fcc8] <instance powerpoint.CurrentUserAtom 'data'> [57fcc8] <instance uint4 'size'> 0x00000014 (20) [57fccc] <instance dynamic.block(4) 'headerToken'> "\x5f\xc0\x91\xe3" [57fcd0] <instance pointer_t 'offsetToCurrentEdit'> *0x86b7e [57fcd4] <instance uint2 'lenUserName'> 0x0005 (5) [57fcd6] <instance uint2 'docfileversion'> 0x03f4 (1012) [57fcd8] <instance ubyte1 'majorVersion'> 0x03 (3) [57fcd9] <instance ubyte1 'minorVersion'> 0x00 (0) [57fcda] <instance block(2) 'unused'> "\x2e\x0c" [57fcdc] <instance pstr.string<char_t> 'ansiUserName'> u'xxxxx' [57fce1] <instance uint4 'relVersion'> 0x00000008 (8) ``` At offset 0x86b7e of the "PowerPoint Document" stream is the UserEditAtom record. This record has a type of 0xff5 and is where the vulnerability begins. This atom is the record that the application uses in order to locate the PersistDirectory records and where a linked list based on the offsetLastEdit field is located. At offset 0x10 of this record type is a pointer to the next UserEditAtom. If this is set to non-zero, the application will continue onto the next UserEditRecord that is described by this offset. For each of the UserEditAtom records, the application will dereference the field at 0x14 to identify the PersistDirectoryAtom containing the list of offsets. The offsets for all of these UserEditAtom records are then aggregated into the buffer that is inreased by powers of two. ``` <class RecordGeneral> '58' [86b7e] <instance Header 'header'> version=0 instance=0x000 type=0x0ff5 length=0x0000001c [86b86] <instance UserEditAtom 'data'> [86b86] <instance sint4 'lastSlideIDRef'> 0x000001a9 (425) [86b8a] <instance uint2 'version'> 0x1fe9 (8169) [86b8c] <instance ubyte1 'minorVersion'> 0x00 (0) [86b8d] <instance ubyte1 'majorVersion'> 0x03 (3) [86b8e] <instance pointer_t<UserEditAtom> 'offsetLastEdit'> *0x836a3 [86b92] <instance pointer_t<PersistDirectoryAtom> 'offsetPersistDirectory'> *0x86b66 [86b96] <instance uint4 'docPersistIdRef'> 0x00000001 (1) [86b9a] <instance uint4 'persistIdSeed'> 0x000000d8 (216) [86b9e] <instance sint2 'lastView'> 0x0001 (1) [86ba0] <instance block(2) 'unused'> "\xc5\x31" ``` When loading the contents of this record type, the application descends into the offsetPersistDirectory record in order to load the different offsets of the edits that have been made to the document. This record contains an array of elements with the following format. This format is a dword where the ficount and id are encoded. The first 12 bits represent the number of offsets that follow, whereas the other 20 bits representing an unique identifier. Immediately following this dword is the array of offsets. This array of offsets is what the vulnerability writes out-of-bounds. Each of these elements then repeat until they meet the size of the record described in it's header. As an example within the provided proof-of-concept, the following 2-element array begins at offset 0x86b66 of the stream. ``` <class RecordGeneral> '*offsetPersistDirectory' [86b66] <instance Header 'header'> version=0 instance=0x000 type=0x1772 length=0x00000010 [86b6e] <instance PersistDirectoryAtom 'data'> [86b6e] <instance 'info[0]'> (0x00100001, 32) info.persistId:0x00001 info.cPersist:0x001 [86b72] <instance PersistDirectoryEntry.offsets 'offsets[0]'> pointer_t[1] [*0x000836c7] [86b76] <instance 'info[1]'> (0x001000bb, 32) info.persistId:0x000bb info.cPersist:0x001 [86b7a] <instance PersistDirectoryEntry.offsets 'offsets[1]'> pointer_t[1] [*0x000857b0] ``` The vulnerability actually occurs due to the application not restricting the number of offsets that it will read out of a list of UserEditAtom records. If an aggressor specifies a list of UserEditAtoms (using the value of offsetLastEdit) that either does not terminate due to it being self-referencing or aggregates a number of offsets (up to a power of 2) in which the target is unable to allocate space for then this will allow an aggressor to corrupt memory at the same power of two relative to the null page. This can be done by using a combination of either multiple PersistDirectory entries with a high count for the number of offsets or multiple UserEdit atoms. ### Crash Information ``` 0:000> lm m jxxtppt jsvda start end module name 06bc0000 06c47000 JXXTPPT C (export symbols) JXXTPPT.DLL 277a0000 27826000 jsvda (export symbols) jsvda.dll 0:004> bl 0 e 06be5c48 0001 (0001) 0:**** JXXTPPT!JXXTPPT_Jsfc_Convert+0x2491d ".printf \"increased to %x\\n\",@eax;gc" 1 d 06bd74fa 0001 (0001) 0:**** JXXTPPT!JXXTPPT_Jsfc_Convert+0x161cf 2 e 06be5bdf 0001 (0001) 0:**** JXXTPPT!JXXTPPT_Jsfc_Convert+0x248b4 "? dwo(@ecx+4);gc" 3 e 06be5bf6 0001 (0001) 0:**** JXXTPPT!JXXTPPT_Jsfc_Convert+0x248cb "? @eax; gc" 0:000> g Evaluate expression: 67108864 = 04000000 Evaluate expression: 0 = 00000000 (e58.c0c): Access violation - code c0000005 (first chance) First chance exceptions are reported before any exception handling. This exception may be expected and handled. eax=02000000 ebx=03f83980 ecx=44976e30 edx=00000000 esi=00259a48 edi=00259a24 eip=06be5c38 esp=00259930 ebp=00259938 iopl=0 nv up ei pl nz ac pe nc cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00210216 JXXTPPT!JXXTPPT_Jsfc_Convert+0x2490d: 06be5c38 890c82 mov dword ptr [edx+eax*4],ecx ds:0023:08000000=d74b0802 0:000> ub . JXXTPPT!JXXTPPT_Jsfc_Convert+0x248f3: 06be5c1e 8d449104 lea eax,[ecx+edx*4+4] 06be5c22 50 push eax 06be5c23 ff150825c206 call dword ptr [JXXTPPT!DRFL_SaveFD3A+0x24a8d (06c22508)] 06be5c29 83c40c add esp,0Ch 06be5c2c 8b4dfc mov ecx,dword ptr [ebp-4] ; *this 06be5c2f 8b5108 mov edx,dword ptr [ecx+8] ; *this+8 06be5c32 8b450c mov eax,dword ptr [ebp+0Ch] 06be5c35 8b4d08 mov ecx,dword ptr [ebp+8] 0:000> ub jxxtppt+25bfc L10 JXXTPPT!JXXTPPT_Jsfc_Convert+0x2489f: 06be5bca c745f801000000 mov dword ptr [ebp-8],1 06be5bd1 8b55f8 mov edx,dword ptr [ebp-8] ; previous size 06be5bd4 d1e2 shl edx,1 06be5bd6 8b45fc mov eax,dword ptr [ebp-4] ; *this 06be5bd9 895004 mov dword ptr [eax+4],edx ; *this+4 06be5bdc 8b4dfc mov ecx,dword ptr [ebp-4] 06be5bdf 8b5104 mov edx,dword ptr [ecx+4] 06be5be2 c1e202 shl edx,2 06be5be5 52 push edx 06be5be6 8b45fc mov eax,dword ptr [ebp-4] 06be5be9 8b4808 mov ecx,dword ptr [eax+8] 06be5bec 51 push ecx 06be5bed ff15f424c206 call dword ptr [JXXTPPT!DRFL_SaveFD3A+0x24a79 (06c224f4)] ; realloc 06be5bf3 83c408 add esp,8 06be5bf6 8b55fc mov edx,dword ptr [ebp-4] ; *this 06be5bf9 894208 mov dword ptr [edx+8],eax ; *this+8 0:000> ? dwo(@ebp-8) ; previous size Evaluate expression: 33554432 = 02000000 0:000> ? poi(poi(@ebp-4)+4) ; current size Evaluate expression: 67108864 = 04000000 0:000> ? poi(poi(@ebp-4)+4) << 2 ; allocation size Evaluate expression: 268435456 = 10000000 0:000> ? dwo(@ebp+C) ; from first argument Evaluate expression: 33554432 = 02000000 0:000> ? dwo(poi(@ebp-4)+8) ; returned from realloc Evaluate expression: 0 = 00000000 ``` ### Timeline * 2016-08-28 - Vendor Disclosure * 2017-02-24 - Public Release ### CREDIT * Discovered by a member of Cisco's Talos team.
last seen2017-11-19
titleIchitaro Word Processor PersistDirectory Code Execution Vulnerability(CVE-2017-2791)


last seen2019-05-29
reporterTalos Intelligence
titleIchitaro Word Processor PersistDirectory Code Execution Vulnerability