Ichitaro Office JTD Figure handling Code Execution Vulnerability(CVE-2017-2789)

2017-09-22T00:00:00
ID SSV:96566
Type seebug
Reporter Root
Modified 2017-09-22T00:00:00

Description

Summary

A vulnerability was discovered within the Ichitaro word processor. Ichitaro is published by JustSystems and is considered one of the more popular word processors used within Japan. Ichitaro's proprietary file format is a Compound Document similar to .doc for Microsoft Word called .jtd. When processing a Figure stream from a .jtd, the application will allocate space when parsing a Figure. When copying filedata into this buffer, the application will calculate two values to determine how much data to copy from the document. If both of these values are larger than the size of the buffer, the application will choose the smaller of the two and trust it to copy data from the file. This value is larger than the buffer size, which leads to a heap-based buffer overflow. This overflow corrupts an offset in the heap used in pointer arithmetic for writing data and can lead to code-execution under the context of the application.

Tested Versions

JustSystems Ichitaro

Product URLs

http://www.ichitaro.com/

CVSSv3 Score

8.8 - CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H

Details

Ichitaro uses the Structured Storage documentation format to read and process a .jtd file. Although there are many streams within a .jtd, the two streams that affect this bug are Figure and FigureData. Figure is the metadata for Figures and FigureData is contents representing the Figure. The modules involved in the vulnerability are below (as described by lm vm in windbg): start end module name 21430000 21479000 jsfdm C (export symbols) C:\Program Files (x86)\JustSystems\JSLIB32\jsfdm.dll File version: 4.5.9.0 Product version: 4.5.9.0 start end module name 213e0000 21402000 jsmisc32 C (export symbols) C:\Program Files (x86)\JustSystems\JSLIB32\jsmisc32.dll File version: 2.7.1.0 Product version: 2.7.1.0 start end module name 14b80000 14bd1000 jsdrfl C (export symbols) C:\Program Files (x86)\JustSystems\JSLIB32\jsdrfl.dll File version: 1.1.2.0 Product version: 1.1.2.0

In the following code, the application isolates a 0x3004 byte chunk out of a previously allocated 0x36b00 chunk (esi). This chunk is used to store FigureData which is processed later. The reason this occurs is to write the size of this chunk after the isolation. This leaves the chunk with the size at the end of the object and a pointer to the end of the object. jsfdm!FDM_inquirePageNumber+0x2a: 2144980e push 3004h // Amount to allocate 21449813 push esi // Chunk to place allocation 21449814 call jsfdm!Ordinal159+0x1440 (21431440) // Calls into following function \ jsfdm!Ordinal159+0x1475: 21431458 mov ecx,dword ptr [esp+10h] // Extract 0x3004 from arg[1] 21431463 lea edi,[ecx+2] // edi = 0x3004 + 2 21431468 movzx esi,di // esi = 0x3006 ... 21431475 add dword ptr [eax+550Eh],esi // Add 0x3006 to our base pointer 2143147b mov eax,dword ptr [eax+550Eh] // Store the base pointer used for writing in eax ... 21431485 mov word ptr [eax-2],di // Store 0x3006 two behind our new pointer

Pseudocode: baseptr = esi currptr = baseptr + 0x3006 // 2 additional bytes needed to store the size *(currptr - 2) = 0x3006 Later in the code, the application will reset the writing pointer to point back to the beginning of the chunk using the aforementioned saved size. jsfdm!Ordinal159+0x1492: 21431492 mov edx,dword ptr [eax+550Eh] // Extract our saved pointer (see 0x21431475 above) 21431498 lea ecx,[eax+550Eh] // Extract the pointer to our saved pointer 2143149e movzx edx,word ptr [edx-2] // Extract the size of the old chunk (0x3006) 214314a2 sub dword ptr [ecx],edx // Rewind the chunk using the extracted chunk size 180b14a4 mov ecx,dword ptr [ecx] // Read pointer from rewound pointer (new_addr)

This new_addr is then saved for later use by Ichitaro. The application can now read the size of a given Figure from the Figure header. This value is multiplied by 6 since each of the elements that will be read later in the program are 6 bytes in length. jsfdm!FDM_inquirePageNumber+0x3b: 2144981f mov ebx,dword ptr [ebp+10h] // Extract the size from Figure header (0xe00) ... 21449828 movsx esi,bx // Sign extend ... 2144982c lea eax,[esi+esi*2] 2144982f lea eax,[eax+eax+0Ah] // size = original_size * 6 + 0xa

From this code, one suspect value is saved and will be returned to later (value_1). There is also a second value calculated based on the FigureData itself.

The application reads the subelement lengths from the FigureData header by reading the second byte of each element representing a number of subelements.

To calculate the possible figure length from this header, an accumulation value starts at 0. The application starts by adding 0x4b4 to the accumulation value and 0x4b0 for each subsequent element, as shown below: jsfdm!FDM_initHWMM+0xd1: continue: 180bfa0b mov eax,dword ptr [ebp+0Ch] // Set eax to current accumulation loop: 180bfa0e lea edx,[eax+4B4h] 180bfa14 lea ecx,[ebx+4] 180bfa17 cmp ecx,edx // Check if accumulated value will be over threshold 180bfa19 jbe jsfdm!FDM_initHWMM+0xef (last_loop) 180bfa1b test eax,eax // First loop eax isn't set 180bfa1d mov edi,4B4h // Set edi to 4b4 for first loop 180bfa22 je jsfdm!FDM_initHWMM+0xfa (first_loop) 180bfa24 add edi,0FFFFFFFCh // Get 4b0 by truncating 0x4b4 + 0xfffffffc 180bfa27 jmp jsfdm!FDM_initHWMM+0xf6 last_loop: 180bfa29 mov edi,ebx 180bfa2b sub edi,eax 180bfa2d add edi,4 // Set edi to the remainder necessary to reach threshold 180bfa30 test eax,eax 180bfa32 jne jsfdm!FDM_initHWMM+0x106 (every_other_loop) first_loop: 180bfa34 lea eax,[ebp-4B4h] 180bfa3a push eax 180bfa3b push edi // Push 4b4 to be accumulated 180bfa3c push 0 // Starting accumulation value 180bfa3e jmp jsfdm!FDM_initHWMM+0x10f (body) every_other_loop: 180bfa40 lea ecx,[ebp-4B0h] 180bfa46 push ecx 180bfa47 push edi // Push 4b0 to be accumulated 180bfa48 push eax // Current accumulation value body: 180bfa49 push dword ptr [esi] // Push address to store accumulation 180bfa4b call jsfdm!FDM_convWTEXTtoTEXT+0xbe4c ... 180bfa5e add dword ptr [ebp+0Ch],eax // Add to local accumulation 180bfa61 lea eax,[ebx+4] 180bfa64 cmp dword ptr [ebp+0Ch],eax // Check if parsed enough 180bfa67 je jsfdm!FDM_initHWMM+0x131 (return(1)) 180bfa69 jb jsfdm!FDM_initHWMM+0xd1 (continue) return(1) 180bfa6b push 1 180bfa6d pop eax 180bfa6e pop edi 180bfa6f pop esi 180bfa70 pop ebx 180bfa71 leave

After saving the possible length using elements, the application also must take into account subelements. Thus, 0x16 is added to the accumulation value for each of the subelements. This final value will be refered to as value_2. jsdrfl!CFigureFileCtrl::LoadFDM+0x22c: 14b83a51 8b459c mov eax,dword ptr [ebp-64h] // Current figure 14b83a54 8b4808 mov ecx,dword ptr [eax+8] 14b83a57 e8e46b0000 call jsdrfl!CFigureFileCtrl::GetCompressBitmap+0x134 (14b8a640) 14b83a5c 8945e0 mov dword ptr [ebp-20h],eax // eax now holds the number of subelements ... 14b83a6b c745dc00000000 mov dword ptr [ebp-24h],0 // Initialize loop counter to zero 14b83a72 e903000000 jmp jsdrfl!CFigureFileCtrl::LoadFDM+0x255 (loop) increment_loop: 14b83a77 ff45dc inc dword ptr [ebp-24h] loop: 14b83a7a 8b45e0 mov eax,dword ptr [ebp-20h] 14b83a7d 3945dc cmp dword ptr [ebp-24h],eax // Check if parsed all subelements ... 14b83c9a 8b4d9c mov ecx,dword ptr [ebp-64h] // Current figure 14b83c9d e86a370000 call jsdrfl!CFigureFileCtrl::putFig (14b8740c) \ jsdrfl!CFigureFileCtrl::putFig+0x21: 14b8742d 898de8feffff mov dword ptr [ebp-118h],ecx // Locally save current figure ... 14b8758f 8b85e8feffff mov eax,dword ptr [ebp-118h] // Retrieve current figure 14b87595 8b4014 mov eax,dword ptr [eax+14h] // Retrieve subelements 14b87598 50 push eax 14b87599 e8aec20300 call jsdrfl!DRFL_ExtSaveFDM+0xcec2 (14bc384c) \ jsfdm!FDM_putFig: 180c0adc 8b442404 mov eax,dword ptr [esp+4] // Get our subelements ... 180c0aed 50 push eax 180c0aee e83a000000 call jsfdm!FDM_putFigEx+0x37 (180c0b2d) \ jsfdm!FDM_putFigEx+0x37: 180c0b2e 8b742408 mov esi,dword ptr [esp+8] // Retrieve subelements ... 180c0b5f 56 push esi 180c0b60 e824000000 call jsfdm!FDM_insertFig+0x1f (180c0b89) \ jsfdm!FDM_insertFig+0x292: 180c0df8 lea eax,[ebx+0Ch] // Subelement data 180c0dfb push eax 180c0dfc push 16h // Push 16 to be accumulated 180c0dfe push dword ptr [ebp-1Ch] // Current accumulation value 180c0e01 lea edi,[ebx+7Ah] ... 180c0e09 push dword ptr [edi] // Push address to store accumulation 180c0e0b call jsfdm!FDM_convWTEXTtoTEXT+0xbe4c (180df252)

Before copying the FigureData into the allocated region, a min(value_1, value_2) occurs to determine the size to copy. jsmisc32!Ordinal501+0xbd: 213f5d8a lea eax,[edi+ebx] // edi = 0; move value_1 into eax 213f5d8d cmp eax,ecx // ecx is value_2; compare value_1 and value_2 213f5d8f jbe jsmisc32!Ordinal501+0xc8 (213f5d95) // Select value_1 if it is lower 213f5d91 sub ecx,edi 213f5d93 mov ebx,ecx // Select value_2 if it is lower

The application will choose the smaller of the two values, regardless if it is actually greater than the original 0x3006 chunk size. Using this value, the application will overwrite the saved chunk size (offset 0x3006 from the above arithmetic). jsmisc32!Ordinal501+0x1ab: 213f5e78 53 push ebx // Push size for memmove 213f5e79 56 push esi 213f5e7a e88cfaffff call jsmisc32!Ordinal510+0x4f7 (213f590b) // Calculate source 213f5e82 50 push eax // Push source 213f5e83 8b4514 mov eax,dword ptr [ebp+14h] 213f5e86 03c7 add eax,edi ... 213f5e8a 50 push eax // Push destination 213f5e8b ff1510d93f21 call dword ptr [jsmisc32!Ordinal489+0x55b3 (213fd910)] // memmove

During the manual unallocation of the 0x3004 chunk from the larger 0x36b00 chunk, the corrupted size value results in a pointer that is read out of bounds of the 0x36b00 initial chunk and is saved for later use. Because this pointer is trusted by the application and reused later, an attacker can potentially gain arbitrary write conditions leading to arbitrary code execution.

Timeline

  • 2016-08-29 - Vendor Disclosure
  • 2017-02-24 - Public Release

CREDIT

  • Discovered by a Talos team member and Cory Duplantis of Cisco Talos.