Talos Vulnerability Report

TALOS-2022-1673

Justsystem Ichitaro Protected Attribute Identifier Use-After-Free Vulnerablity

April 5, 2023
CVE Number

CVE-2022-43664

SUMMARY

A use-after-free vulnerability exists within the way Ichitaro Word Processor 2022, version 1.0.1.57600, processes protected documents. A specially crafted document can trigger reuse of freed memory, which can lead to further memory corruption and potentially result in arbitrary code execution. An attacker can provide a malicious document to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

Ichitaro 2022 1.0.1.57600
Versions of relevant binaries:

JSTARO25.OCX
File version: 1.0.1.58105

jsvda.dll
File version: 3.3.321.1

jsmisc32.dll
File version: 2.7.1.0

taro32.exe
File version: 1.0.1.57600

T32com.dll
File version: 1.0.0.200

PRODUCT URLS

Ichitaro - https://www.ichitaro.com/

CVSSv3 SCORE

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

CWE

CWE-416 - Use After Free

DETAILS

Ichitaro is a word processor produced by JustSystems which showcases the ATOK input method system and occupies a large share of the Japanese word-processing market. The Ichitaro word processor supports compatibility with many document formats and provides a broad set of features, allowing it to remain competitive with other available word processors.

Other than the typical document and spreadsheet formats that are provided by the Microsoft Office suite, Ichitaro also supports its native document format, which uses the file extension .JTD. This file format is based on Microsoft’s Structured Storage format, developed as part of Microsoft’s Component Object Model (COM).

After the application has initialized its reference allocator and used it to allocate an object for the path to open, it will return a handle using the T32com.dll library. This handle will be used to read the contents of a Structured Storage document from the location that the path handle represents. Afterwards, the application will use the same handle to the path object to make a tagged allocation for an object that contains the contents of the document. As part of this process, the JSVDA.dll library will construct two arenas that contain arrays representing the list of properties that are associated with each document. These arenas are populated by combining a static array of properties defined in the JSVDA.dll library with an array of properties that are decoded from the “\4JSRV_SummaryInformation” stream and can be accessed by either a 32-bit identifier or a utf-16 encoded description. The priority of these properties can have an effect on the vulnerability described by this document. Throughout the application, each of these related objects will be referenced by their handles.

The following method inside the JSTARO25.OCX library will extract information from the document handle, and the object it was passed, in order to determine if the document needs to be converted or processed in any way in order to load its contents. At [1], the method will initialize space within the current object in preparation to write a string, followed by checking various fields of the object within the first parameter against strings that are stored within the generated JSVDA.TBL file. This file contains various rules that are used to detect the file type and determine the correct library to use when converting the current document. With the given proof-of-concept, the fields at [2] will be assigned. Afterwards at [3], the current method will then check two 16-bit values at offset +0x5e and +0x60 of an object before taking a branch, and then at [4] the document handle will be passed to a method call, which will use the document handle to fetch an attribute from the document.

3c1fd148: push ebp
3c1fd149: mov ebp, esp
3c1fd14b: sub esp, 1Ch
...
3c1fd166: mov eax, [esi+CCustomFile.v_data_64.p_stackObject_2c4]
3c1fd16c: push offset str.null_9c5368
3c1fd171: mov eax, [eax+stackobject_799a4b.field_40]
3c1fd174: mov [ebp+var_4], eax
3c1fd177: lea eax, [esi+CCustomFile.v_data_64.vw_fileTypeString(c)_2e0]
3c1fd17d: push eax
3c1fd17e: call jsmisc32::wcscpy_1efb                                        ; [1] initialize string buffer in current object
3c1fd183: mov ebx, [ebp+ap_vfk_0]
3c1fd186: pop ecx
3c1fd187: pop ecx
3c1fd188: test byte ptr [ebx+object_VFK.vw_fileInfo(0)_0], 2                ; check file information
3c1fd18b: jz loc_3C1FD2CD
...
3c1fd328: mov eax, [esi+CCustomFile.v_stdio_4.p_stdioObject?_8]
3c1fd32b: mov [esi+CCustomFile.v_data_64.v_notTemplateType?_220], 5         ; [2] assign document types to properties of current object
3c1fd335: mov [esi+CCustomFile.v_data_64.v_documentType?_2a4], 6            ; [2] assign document types to properties of current object
3c1fd33f: push 2
3c1fd341: test byte ptr [eax+CStdioFile.v_data_4.field_38], 1
3c1fd345: pop ecx
3c1fd346: jz short loc_3C1FD353
...
3c1fd422: movzx ecx, [ebx+object_VFK.field_5E]                              ; [3] check field
3c1fd426: xor eax, eax
3c1fd428: inc eax
3c1fd429: cmp ax, cx
3c1fd42c: jnz short loc_3C1FD442
3c1fd42e: mov eax, 0B00h
3c1fd433: cmp ax, [ebx+object_VFK.field_60]                                 ; [3] compare field against 0xb00
3c1fd437: jnz short getAttribute(32000001)_77d485                           ; [3] branch to code for getting an attribute
...
3c1fd485: push [ebp+av_docIndex_4]                                          ; document handle
3c1fd488: mov ecx, [esi+CCustomFile.v_stdio_4.p_stdioObject?_8]
3c1fd48b: call CStdioFile::getAttribute(32000001)_77cebc                    ; [4] method to get attribute 0x32000001
3c1fd490: test eax, eax
3c1fd492: jz loc_3C1FD689

The following method is simply a wrapper around the JSVDA.dll library that uses the document handle to fetch an attribute from the arenas that are associated with the document. Upon entering the method at [5], the tagged allocator will be used to allocate an object that will be used for querying the document arena for a particular attribute.

3c1fcedc: lea eax, [ebp+lp_viiObject_4]
3c1fcedf: push eax
3c1fcee0: push esi
3c1fcee1: push 'IIV'
3c1fcee6: push 1
3c1fcee8: call StgThreadSafeTaggedObjectAllocator_16b4a     ; [5] allocate space for attribute object
3c1fceed: add esp, 10h
3c1fcef0: test eax, eax
3c1fcef2: js short return(@esi)_77cf2b

After having used the tag allocator to allocate space for an object used to fetch an attribute from the document’s arena by its identifier, at [6] the current method will write an integer, 0x32000001, to offset +0x0 of the object and then pass it with an address to write the resulting attribute at [7] to the function call at [8]. It is this function call that enters the path that exercises the vulnerability described by this document. After the function call returns, the branch at [9] will check if it has failed and proceed to exit the method if it fails. As part of the clean up process of the current method, the method will check each of the pointers that it allocated and release them if they’re not null. As [10], the method will check the address of the allocation that was made to store the attribute identifier and then release it. Immediately following the release of the attribute object, the same logic will be performed on the result that was written by the function call at [8]. After checking if the pointer is non-null at [11], the code will then execute the function call at [12] to release the memory that was allocated.

3c1fcef4: mov eax, [ebp+lp_viiObject_4]
3c1fcef7: push esi
3c1fcef8: mov [eax+object_6b3a6.v_identifier_0], 32000001h                  ; [6] assign 0x32000001 to object field
3c1fcefe: lea eax, [ebp+lp_resultPointer_8]
3c1fcf01: push eax                                                          ; [7] result pointer related to vulnerability
3c1fcf02: push [ebp+lp_viiObject_4]                                         ; tagged object
3c1fcf05: push esi
3c1fcf06: push edi                                                          ; document handle
3c1fcf07: call jsvda::object_6b3a6::getDocumentAttributeFromBucket_6beb4    ; [8] get attribute from arena
3c1fcf0c: add esp, 14h
3c1fcf0f: test eax, eax
3c1fcf11: js short return(@esi)_77cf2b                                      ; [9] branch on failure
...
3c1fcf2b: mov eax, [ebp+lp_resultPointer_8]
3c1fcf2e: cmp [ebp+lp_viiObject_4], 0
3c1fcf32: jz short return(@esi)_77cf40
3c1fcf34: push [ebp+lp_viiObject_4]                                         ; [10] load address of object containing attribute
3c1fcf37: call StgThreadSafeTaggedObjectRelease_17791                       ; [10] release the object
...
3c1fcf3c: mov eax, [ebp+lp_resultPointer_8]                                 ; [11] load result pointer
3c1fcf3f: pop ecx
3c1fcf40: test eax, eax                                                     ; [11] check if result is null
3c1fcf42: jz short return(@esi)_77cf4b                                      ; [11] branch if null
3c1fcf44: push eax
3c1fcf45: call StgThreadSafeTaggedObjectRelease_17791                       ; [12] release the pointer that was stored to result
3c1fcf4a: pop ecx
3c1fcf4b: pop edi
3c1fcf4c: mov eax, esi
3c1fcf4e: pop esi
3c1fcf4f: leave
3c1fcf50: retn 4

When trying to fetch a document attribute by its identifier, the following code will be used. The first thing the function will do is use the handle that was passed as the first parameter to fetch the “object_34605” that represents the document using the function call at [13]. After the “object_34605” has been returned, the logic that follows will load the attribute object that was passed in its 3rd parameter in order to fetch the field at offset +0x0. This field contains the attribute identifier to search for which is the value 0x32000001. At [14], the function will mask it with the value 0x0F0000000 and then check to see if the result is 0x020000000. If the value matches, then the branch immediately afterwards will be taken.

2780beb4: push ebp
2780beb5: mov ebp, esp
...
2780bebb: lea eax, [ebp+lp_docObject_4]
2780bebe: push edi
2780bebf: push eax                                          ; result object_34605
2780bec0: push 4                                            ; object type
2780bec2: push [ebp+av_index_0]                             ; document handle
...
2780becd: call jsvda::global_79ac0::getFromBucket_6253d     ; [13] get object_34605 from document handle
2780bed2: mov ebx, eax
2780bed4: add esp, 0Ch
2780bed7: cmp ebx, edi
2780bed9: jl loc_2780C01A
...
2780beff: mov esi, [ebp+ap_hvObject_8]                      ; attribute object from parameters
2780bf02: mov eax, 0F000000h
2780bf07: mov [ebp+var_8], 1
2780bf0e: mov ecx, [esi+object_6b3a6.v_identifier_0]        ; [14] fetch attribute identifier from attribute object field
2780bf10: mov edx, ecx
2780bf12: and edx, eax
2780bf14: jz loc_2780BFA0
...
2780bf22: cmp edx, 2000000h                                 ; [14] check the attribute identifier
2780bf28: jz short loc_2780BF38                             ; branch taken

Once the branch that checks a mask of the attribute identifier is taken, it will lead to the following code. This code will simply extract the arena descriptor of the document and use it, along with the attribute object containing the identifier to search for, as parameters for the function call at [15]. If the desired identifier is found, the value of the attribute will be written to the last parameter along with the size of the value at the second-last parameter.

2780bf38: mov edi, [ebp+ap_resultPointer_c]
2780bf3b: push edi                                                      ; result pointer of attribute value
2780bf3c: push [ebp+ap_resultSize_10]                                   ; result size of attribute value
2780bf3f: push esi                                                      ; attribute object
2780bf40: push [ebx+object_34605.v_arenaField_48.p_arena_0]             ; document attribute arena descriptor
2780bf43: call sub_277F3350                                             ; [15] call function to search for desired attribute
2780bf48: add esp, 10h
2780bf4b: cmp dword ptr [esi+object_6b3a6.v_identifier_0], 31200001h
2780bf51: mov ebx, eax
2780bf53: mov ecx, 800F0000h
2780bf58: jnz short loc_2780BF75

At the beginning of the function that is used to fetch the 0x32000001 attribute is the following code. First at [16], the function will initialize the result pointer with 0x0 before passing the current attribute object, along with the document arena, to the function call at [17]. At the beginning of the function, the object with attribute information will be converted and copied into a local variable on the stack using the function call at [18]. Afterwards, this local variable will be used with the function call at [19] to scan the document’s arena for a matching attribute. After a match has been found, the result will then be passed at the function call at [20] in order to copy the result back into the object represented by the 2nd parameter of the function. This function call contains a side effect that is directly related to this vulnerability.

277f3360: and dword ptr [eax], 0                                                    ; [16] initialize result pointer
...
277f3363: mov ebx, [ebp+ap_hvObject_4]                                              ; attribute object
277f3366: cmp [ebx+object_6b3a6.v_identifier_0], 3110000Dh
277f336c: jnz short loc_277F3382
...
277f339b: push ebx                                                                  ; attribute object
277f339c: push [ebp+ap_arena_0]                                                     ; document attribute arena descriptor
277f339f: call arena::reinitializeObject(IIV)_6aa3e                                 ; [17] \ update fields of attribute object
277f33a4: mov esi, eax
277f33a6: xor edi, edi
277f33a8: pop ecx
277f33a9: cmp esi, edi
277f33ab: pop ecx
277f33ac: jl return(@esi)_53546
\
2780aa3e: push ebp
2780aa3f: mov ebp, esp
2780aa41: sub esp, 60h
2780aa44: push esi
2780aa45: lea eax, [ebp+lv_shallowHvObject_60]                                      ; local variable for attribute information
2780aa48: push [ebp+ap_hvObject_4]                                                  ; source attribute object
2780aa4b: push eax                                                                  ; destination variable for attribute information
2780aa4c: call struc_6a4f0::ConvertToHashValueObject?_6a4f0                         ; [18] copy attribute object into local variable
...
2780aa51: pop ecx
2780aa52: lea eax, [ebp+lv_shallowHvObject_60]
2780aa55: pop ecx
2780aa56: push 0
2780aa58: push eax                                                                  ; local variable with attribute information
2780aa59: push 0FFFFFFFFh
2780aa5b: push [ebp+ap_arena_0]                                                     ; attribute arena descriptor 
2780aa5e: call ids_wmm::scanForIndexAndIdentifierSorta_18c11                        ; [19] scan arena for local variable with attribute information
...
2780aa6c: lea eax, [ebp+lv_shallowHvObject_60]
2780aa6f: push 0
2780aa71: push eax                                                                  ; local variable with attribute information
2780aa72: push [ebp+ap_hvObject_4]                                                  ; destination attribute object
2780aa75: call object_6b3a6::updateObjectWithGlobalAttributeByIdentifier_6a71f      ; [20] update attribute object with result from scanning arena

After the attribute arena has been scanned for an object, the function will update the attribute object with the fields that were found in the arena. It is this function that has an error condition that can result in the attribute object not being copied correctly. The following code shows the implementation of this function. First at [21], the attribute object will be initialized with 0 using the memset function. Afterwards, each of the original fields from the second parameter will be copied into the first parameter. At [22], however, the identifier from the object will be stored to a local variable on the stack due to the function using it as a key when trying to fetch the object from the document’s arena. At [23], the function call will use this local variable to match against the attributes in the document’s attribute arena, using the identifier as a result of the first parameter being set to 1. After the match has been found, at [24] the function will copy the description from the result into the object that was passed as a parameter. Afterwards, the function will then check a field at offset +0x44 of the source object that was passed as a parameter. If this field, when masked with 0x0F000000, is not equal to 0x04000000 or 0x02000000, then the function will return without having initialized the identifier attribute at field +0x0 of the object in the first parameter. As a result of this, the identifier will be left as 0x00000000 and will satisfy the necessary conditions to trigger this vulnerability.

2780a71f: push ebp
2780a720: mov ebp, esp
2780a722: sub esp, 68h
...
2780a728: mov edi, [ebp+ap_hvDestObject_0]                      ; parameter containing attribute object
2780a72b: xor ebx, ebx
2780a72d: push size object_6b3a6
2780a72f: push ebx
2780a730: push edi                                              ; [21] initialize all fields of attribute object
2780a731: call memset
...
2780a736: mov esi, [ebp+ap_sourceStruc_4]
2780a739: add esp, 0Ch
2780a73c: mov eax, [esi+struc_6a4f0.v_important_44]             ; load field at +0x44
2780a73f: mov [edi+object_6b3a6.v_important_4], eax             ; store value to field at +0x4
2780a742: mov eax, [esi+struc_6a4f0.v_someType?_48]             ; load field at +0x48
2780a745: mov [edi+object_6b3a6.v_someType?_48], eax            ; store value to field at +0x48
2780a748: mov eax, [esi+struc_6a4f0.v_extra1_50]                ; load attribute value size from field at +0x50
2780a74b: mov [edi+object_6b3a6.v_extra1size_4c], eax           ; store attribute value size to field at +0x4c
2780a74e: mov eax, [esi+struc_6a4f0.v_identifier_0]             ; load identifier at +0x0
2780a750: mov [ebp+lv_fileAttribute_68.v_identifier?_44], eax   ; [22] store into field at +0x44
2780a753: mov eax, [ebp+ap_resultStringIndexMaybe_8]
2780a756: cmp eax, ebx
2780a758: jz short loc_2780A75F
...
2780a75f: lea eax, [ebp+lv_fileAttribute_68]                    ; use local variable to match attribute in arena
2780a762: push eax                                              ; address of local variable
2780a763: push 1                                                ; match by attribute identifier
2780a765: call global_73db8::getFileAttributeByMatchType_6a5b9  ; [23] update second parameter with attribute matching identifier
...
2780a787: lea eax, [esi+struc_6a4f0.v_wDescription?_4]
2780a78a: push eax
2780a78b: lea eax, [edi+object_6b3a6.v_wDescription?_8]
2780a78e: push eax
2780a78f: call wstrlcpy?_6a63d                                  ; [24] copy description from source attribute name to result attribute object
...
2780a794: mov eax, [esi+struc_6a4f0.v_important_44]             ; load field from offset at +0x44
2780a797: add esp, 0Ch
2780a79a: and eax, 0F000000h                                    ; mask value against 0x0F000000
2780a79f: cmp eax, 4000000h
2780a7a4: jz short loc_2780A7AD
2780a7a6: cmp eax, 2000000h
2780a7ab: jnz short return(80030057)_6a7b3                      ; [25] if value not found, then return error code

After the attribute object has been populated with its identifier still set to 0x00000000, and when the function returns, the following code will be executed. This code will extract a type from the field at +0x48 of the attribute object at [26]. Depending on the type being checked, one of the branches at [27] will be taken. Finally at [28], a size will be extracted from the attribute object that was populated from the arena. This size will then be used with the tagged allocator at [29] to allocate memory, which will be returned by writing the pointer to the new memory to its second parameter. This memory being allocated is intended to store the value of the attribute that is being looked up by its identifier. Due to it being written to the parameter directly, there will be two references to the same memory.

277f33b2: mov eax, [ebx+object_6b3a6.v_someType?_48]        ; [26] check type
277f33b5: cmp eax, edi
277f33b7: jle short allocateAttributeValue_533f4            ; [27] take branch
277f33b9: cmp eax, 0Ah
277f33bc: jle short loc_277F33C3
277f33be: cmp eax, 13h
277f33c1: jnz short allocateAttributeValue_533f4            ; [27] take branch
...
277f33f4: mov eax, [ebx+object_6b3a6.v_extra1size_4c]       ; [28] extract size
277f33f7: cmp eax, edi
277f33f9: jbe short loc_277F3435
...
277f33fb: push [ebp+ap_resultPointer_c]                     ; [29] result allocation
277f33fe: push eax                                          ; extracted size from attribute object
277f33ff: call TaggedObjectAllocate(BIN)_18bd6              ; [29] allocate memory of given size
277f3404: mov esi, eax
277f3406: pop ecx
277f3407: cmp esi, edi
277f3409: pop ecx
277f340a: jl return(@esi)_53546

After the memory has been allocated for the value of the attribute, the following code will pass the arena descriptor, the attribute object that is missing its identifier, pointers for returning the attribute size and its data to the function call at [30]. This function will use the attribute object in order to read the value for the attribute into the buffer that was just allocated. Due to the object’s identifier being set to 0x00000000 as a result of the field at +0x44 not being set to the correct value, this function call will return an error, which will result in the branch at [31] being taken. This branch will free the memory that is intended to contain the attribute’s value before returning. Due to the application having already written the memory to the address referenced by its parameter, this, combined with the releasing of the tagged memory in the JSTARO25.OCX library, will result in a double-free vulnerability.

277f3410: lea eax, [ebp+ap_hvObject_4]
277f3413: push eax
277f3414: mov eax, [ebp+ap_resultPointer_c]
277f3417: push dword ptr [eax]                          ; attribute value destination
277f3419: push [ebx+object_6b3a6.v_extra1size_4c]       ; attribute value size
277f341c: push edi
277f341d: push ebx                                      ; attribute object
277f341e: push [ebp+ap_arena_0]                         ; document arena
277f3421: call sub_277F161B                             ; [30] get attribute value using attribute object
277f3426: mov esi, eax
277f3428: add esp, 18h
277f342b: cmp esi, edi
277f342d: jl return(@esi)_53546                         ; [31] error branch
...
277f3546: mov edi, [ebp+ap_resultPointer_c]             ; load address of pointer
277f3549: push dword ptr [edi]                          ; dereference pointer
277f354b: call StgThreadSafeTaggedObjectRelease_18c04   ; [32] free memory that was just allocated
277f3550: pop ecx
277f3551: jmp short return(@esi)_53531
...
277f3531: mov eax, esi
277f3533: pop edi
277f3534: pop esi
277f3535: pop ebx
277f3536: leave
277f3537: retn

Crash Information

After the document has been modified by the code within proof-of-concept, it may then be opened by the application using a debugger such as WinDbg. In the following steps, the WinDbg debugger is used and the libraries used by the application are mapped at the following addresses.

Browse full module list
start    end        module name
54660000 55597000   JSTARO25   (export symbols)       JSTARO25.OCX
277a0000 27826000   jsvda      (export symbols)       jsvda.dll
213e0000 21402000   jsmisc32   (deferred)             
01290000 015bd000   taro32     (deferred)     
3c7c0000 3eef5000   T32com     (deferred)             

Once the document has been opened by the application, the address where the handle for a path is allocated can be navigated to. This will call the function at 0x278022ef with the path to the document being opened in order to construct an “object_78250” and write its handle to the last parameter.

0:007> g jsvda+47f06
...
eip=277e7f06 esp=0a63f75c ebp=0a63f798 iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000282
jsvda!Ordinal253+0x2095:
277e7f06 e8e4a30100      call    jsvda!Ordinal20 (278022ef)

0:007> dc @esp L4
0a63f75c  ffffffff 100000a4 06bd9580 06beb64e  ............N...
0:007> du poi(@esp+4*2)
06bd9580  "C:\path\to\document.jtd"

After stepping over the function, the function will write the handle for the path to its last parameter. In this case, the handle for the path is 0x47

0:007> dc poi(@esp+4*3) L1
06beb64e  0074006a                             j.t.

0:007> p
...
eax=00000000 ebx=06bf1848 ecx=06beb64e edx=e302e944 esi=06b1f478 edi=0109ed74
eip=277e7f0b esp=0a63f75c ebp=0a63f798 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
jsvda!Ordinal253+0x209a:
277e7f0b 83c410          add     esp,10h

0:007> dc 6beb64e L1
06beb64e  00000047                             G...

Once the handle has been allocated, we can navigate to the part of the application that will use the path handle to create an “object_34605” for the document. The path handle (0x47) will be passed as the second parameter. In this function, the fifth parameter will contain the destination that the constructed “object_34605” will be written to.

0:007> g jstaro25+77db09
...
eax=00dc9d8c ebx=00dc9fb4 ecx=00001050 edx=00e8f000 esi=00000047 edi=04b29e98
eip=54dddb09 esp=00dc9d5c ebp=00dc9f98 iopl=0         nv up ei ng nz na po cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200283
JSTARO25!DllUnregisterServer+0x348542:
54dddb09 e8e0bd90ff      call    JSTARO25+0x898ee (546e98ee)

0:000> dc @esp L7
00dc9d5c  00110005 00000047 0109fab4 00000000  ....G...........
00dc9d6c  00000000 00dc9fb4 00dc9d8c           ............

0:000> dc poi(@esp+4*5) L1
00dc9fb4  ffffffff                             ....

Inside the library is the following code, which is directly responsible for constructing the “object_34605”. This function call takes 7 parameters and writes its constructed object to the 7th parameter (0xdc9d50). The third parameter (0x49eddb8) contains the “object_78250” that was determined from the path handle (0x47) that was given as a parameter. We will be returning to the instruction following this call instruction later.

0:000> g jsvda+683a9
...
eax=00000005 ebx=00000000 ecx=049eddb8 edx=00e8f000 esi=00000000 edi=00110005
eip=278083a9 esp=00dc9d14 ebp=00dc9d54 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
jsvda!Ordinal203+0x127:
278083a9 e857c2fcff      call    jsvda!Ordinal521+0x42d (277d4605)

0:000> dc @esp L7
00dc9d14  00000005 00000000 049eddb8 0109fab4  ................
00dc9d24  00000000 00000000 00dc9d50           ........P...

0:000> * Store the parameter address in the temporary $t1 register
0:000> r@$t1 = poi(@esp+4*6)

Examining the header of the “object_78250” shows that the field at offset +0x10 contains the path handle (0x47).

0:000> !py print(itchi.threadSafeHeader(offset=0x049eddb8-0x20).l)
<class itchi.threadSafeHeader> 'unnamed_9a187a8' {unnamed=True}
[49edd98] <instance c(ptype.pointer_t<itchi.threadSafeHeader>) 'p_listEntry_0'> *0x0
[49edd9c] <instance itchi.u32 'v_count_4'> 0x00000002 (2)
[49edda0] <instance itchi.u16 'vw_flags_8'> 0x0001 (1)
[49edda2] <instance itchi.u16 'vw_sizeIndex_a'> 0x0007 (7)
[49edda4] <instance itchi.u32 'v_tag_c'> 0x48544150 (1213481296)
[49edda8] <instance itchi.u32 'v_bucketIndex_10'> 0x00000047 (71)
[49eddac] <instance itchi.u32 'pf_release_14'> 0x27807f69 (662732649)
[49eddb0] <instance itchi.u32 'field_18'> 0x00000000 (0)
[49eddb4] <instance itchi.u32 'v_size_1c'> 0x000000c0 (192)

Afterwards, we can navigate to the part of the library that will allocate an arena for the attributes of the document. At the entry point of the function that is responsible for allocating the arena is the following instruction. This function takes two parameters: the first one is the destination to write a descriptor for the arena that being allocated; the second one is the arena type (0x11211111), which is determined by examining the streams within the structured storage document.

0:007> g jsvda+69660
...
eax=00dc9c88 ebx=06ae8fc0 ecx=0a841578 edx=00000003 esi=00dc9cd4 edi=00000000
eip=27809660 esp=00dc9bdc ebp=00dc9c68 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
jsvda!Ordinal929+0x11d4:
27809660 55              push    ebp

0:000> dc @esp+4 L2
00dc9be0  00dc9cd4 11211111                    ......!.
0:000> dc @$p L1
00dc9cd4  ffffffff                             ....

Upon returning, we can examine the address of the first parameter to see the pointer to the arena descriptor for the document (0x27831270). This descriptor will get written into the “object_34605” being constructed.

0:000> pt
eax=00000000 ebx=06ae8fc0 ecx=ffffffff edx=00000000 esi=00dc9cd4 edi=00000000
eip=27809720 esp=00dc9bdc ebp=00dc9c68 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
jsvda!Ordinal929+0x1294:
27809720 c3              ret

0:000> dc dc9cd4 L1
00dc9cd4  27831270                             p..'

After continuing execution to the instruction that follows the call instruction at jsvda+683a9, we can then examine the address that was written to its last parameter to see the address of the “object_34605” that was created (0x6ae8f78).

0:000> g jsvda+683ae
...
eax=00000000 ebx=00000000 ecx=00dc9d50 edx=00e8f000 esi=00000000 edi=00110005
eip=278083ae esp=00dc9d14 ebp=00dc9d54 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
jsvda!Ordinal203+0x12c:
278083ae eb25            jmp     jsvda!Ordinal203+0x153 (278083d5)

0:000> dc @$t1 L1
00dc9d50  06ae8f78                             x...

Examining the header at -0x20 from the “object_34605” (0x6ae8f58) shows the following memory. Field +0x10 of this header will contain the handle of the document object. This document handle (0x4b) will then be used by the application to reference this object.

0:000> !py print(itchi.threadSafeHeader(offset=0x6ae8f78-0x20).l)
<class itchi.threadSafeHeader> 'unnamed_93bee68' {unnamed=True}
[6ae8f58] <instance c(ptype.pointer_t<itchi.threadSafeHeader>) 'p_listEntry_0'> *0x0
[6ae8f5c] <instance itchi.u32 'v_count_4'> 0x00000001 (1)
[6ae8f60] <instance itchi.u16 'vw_flags_8'> 0x0004 (4)
[6ae8f62] <instance itchi.u16 'vw_sizeIndex_a'> 0x0005 (5)
[6ae8f64] <instance itchi.u32 'v_tag_c'> 0x00434f44 (4411204)
[6ae8f68] <instance itchi.u32 'v_bucketIndex_10'> 0x0000004b (75)
[6ae8f6c] <instance itchi.u32 'pf_release_14'> 0x277b8fa9 (662409129)
[6ae8f70] <instance itchi.u32 'field_18'> 0x00000000 (0)
[6ae8f74] <instance itchi.u32 'v_size_1c'> 0x00000058 (88)

Examining the “object_34605” object itself shows that at offset 0x48 will be the address of the arena descriptor (0x27831270), and at offset 0x50 will be the address of the “object_78250” (0x49eddb8) for the path.

0:000> !py print(itchi.object_34605(offset=0x6ae8f78).l)
<class itchi.object_34605> 'unnamed_93d4d18' {unnamed=True}
[6ae8f78] <instance itchi.u32 'field_0'> 0x00000000 (0)
[6ae8f7c] <instance c(ptype.pointer_t<itchi.ReferenceCountHeader>) 'p_refCountHeader?_4'> *0x6bd99c0
...
[6ae8fc0] <instance itchi.object_34605__field_48 'v_arenaField_48'> "\x70\x12\x83\x27\x00\x00\x00\x00\xb8\xdd\x9e\x04\x00\x00\x00\x00"

0:000> !py print(itchi.object_34605(offset=0x6ae8f78).l['v_arenaField_48'])
<class itchi.object_34605__field_48> 'v_arenaField_48'
[6ae8fc0] <instance c(ptype.pointer_t<itchi.global_1c5d0_arena>) 'p_arena_0'> *0x27831270
[6ae8fc4] <instance itchi.u32 'p_4'> 0x00000000 (0)
[6ae8fc8] <instance c(ptype.pointer_t<itchi.object_78250>) 'p_pathObject_8'> *0x49eddb8
[6ae8fcc] <instance itchi.u32 'p_c'> 0x00000000 (0)

At this point the path handle has been associated with the “object_34605” document object, and an arena descriptor has been loaded with the necessary attributes for the document type. We can then navigate to the part of the application that attempts to fetch the 0x32000001 (hash value for protected item) attribute. This function takes a single parameter containing the document handle (0x4b).

0:000> g jstaro25+77cebc
...
eax=00000b00 ebx=0a841578 ecx=04396068 edx=00000001 esi=04b29e98 edi=00000000
eip=54ddcebc esp=00dc9d38 ebp=00dc9d68 iopl=0         nv up ei ng nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200287
JSTARO25!DllUnregisterServer+0x3478f5:
54ddcebc 55              push    ebp

0:000> dc @esp+4 L1
00dc9d3c  0000004b                             K...

Later within this function will be the following call instruction. This instruction takes 5 parameters. It is used to look up an attribute in the document referenced by the document handle in the first parameter (0x4bv), using an object that is passed in its third parameter (0x6ae9000), before it writes the result to its fourth parameter (0xdc9d2c). The pointer that gets written to the address in the fourth parameter is directly related to the vulnerability described by this document.

0:000> g jstaro25+77cf07
...
eax=00dc9d2c ebx=0a841578 ecx=00dc9d30 edx=00000000 esi=00000000 edi=0000004b
eip=54ddcf07 esp=00dc9d10 ebp=00dc9d34 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
JSTARO25!DllUnregisterServer+0x347940:
54ddcf07 e8c44291ff      call    JSTARO25!DllCanUnloadNow+0x2c4 (546f11d0)

0:000> dc @esp L5
00dc9d10  0000004b 00000000 06ae9000 00dc9d2c  K...........,...
00dc9d20  00000000                             ....

0:000> dc poi(@esp+4*3) L1
00dc9d2c  00000000                             ....

0:000> * Store the pointer address to the temporary $t1 register
0:000> r@$t1=poi(@esp+4*3)

Examining the second parameter (0x6ae9000) shows the contents of the object being used to fetch the 0x32000001 (hash value for protected item) attribute.

0:000> !py print(itchi.object_6b3a6(offset=pykd.expr('poi(@esp+4*2)')).l)
<class itchi.object_6b3a6> 'unnamed_93d4868' {unnamed=True}
[6ae9000] <instance itchi.u32 'v_identifier_0'> 0x32000001 (838860801)
[6ae9004] <instance itchi.u32 'v_important_4'> 0x00000000 (0)
[6ae9008] <instance c(pstr.wstring<wchar_t<utf-16-le>>) 'v_wDescription?_8'> (16) '\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
...
[6ae9048] <instance itchi.u32 'v_someType?_48'> 0x00000000 (0)
[6ae904c] <instance itchi.u32 'v_extra1size_4c'> 0x00000000 (0)
...
[6ae9060] <instance itchi.u32 'field_60'> 0x00000000 (0)

Afterwards, we can navigate to the following code, which calls a function that is responsible for converting a document handle into a document object. This function takes three parameters, where the first one is the document handle (0x4b) and the last one is where to write the “object_34605” being fetched.

0:000> g jsvda+6becd
...
eax=00dc9d04 ebx=0a841578 ecx=00dc9d30 edx=00000000 esi=00000000 edi=00000000
eip=2780becd esp=00dc9ce8 ebp=00dc9d08 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
jsvda!Ordinal58+0x19:
2780becd e86b66ffff      call    jsvda!Ordinal946 (2780253d)

0:000> dc @esp L3
00dc9ce8  0000004b 00000004 00dc9d04           K...........

0:000> dc poi(@esp+4*2) L1
00dc9d04  00000000                             ....

After stepping over this function call, we can see that a pointer has been written into the third parameter (0x6ae8f78). This pointer references the same “object_34605” that was previously constructed using the path handle.

0:000> p
eax=00000000 ebx=0a841578 ecx=ffffffff edx=00e8f000 esi=00000000 edi=00000000
eip=2780bed2 esp=00dc9ce8 ebp=00dc9d08 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
jsvda!Ordinal58+0x1e:
2780bed2 8bd8            mov     ebx,eax

0:000> dc dc9d04 L1
00dc9d04  06ae8f78                             x...

Deeper into the function is the following call instruction. This call instruction takes two parameters. The first parameter is the arena descriptor (0x27831270) that was stored at offset +0x48 of the “object_34605”, and the second parameter contains an object with the identifier that is being searched for.

0:000> g jsvda+5339f
eax=00dc9d2c ebx=06ae9000 ecx=00000000 edx=02000000 esi=06ae9064 edi=00dc9cd8
eip=277f339f esp=00dc9c60 ebp=00dc9cdc iopl=0         nv up ei pl nz ac po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200212
jsvda!Ordinal968+0x20cd:
277f339f e89a760100      call    jsvda!Ordinal929+0x25b2 (2780aa3e)

0:000> dc @esp L2
00dc9c60  27831270 06ae9000                    p..'....

Once the object has been searched for, the following call instruction will be used to update its parameter with the attribute that was found when scanning the arena identified by the descriptor (0x27831270). This function takes three parameters, where the first parameter contains the object to update with the found attribute, and the second parameter is the attribute to use. It is probably worth noting that the function containing this call instruction discards its result prior to returning.

0:000> g jsvda+6aa75
eax=00dc9bf8 ebx=06ae9000 ecx=00000000 edx=00000000 esi=00000001 edi=00dc9cd8
eip=2780aa75 esp=00dc9be8 ebp=00dc9c58 iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
jsvda!Ordinal929+0x25e9:
2780aa75 e8a5fcffff      call    jsvda!Ordinal929+0x2293 (2780a71f)

0:000> dc @esp L3
00dc9be8  06ae9000 00dc9bf8 00000000           ............

0:000> u . L5
jsvda!Ordinal929+0x25e9:
2780aa75 e8a5fcffff      call    jsvda!Ordinal929+0x2293 (2780a71f)
2780aa7a 83c40c          add     esp,0Ch
2780aa7d 8bc6            mov     eax,esi
2780aa7f 5e              pop     esi
2780aa80 c9              leave
2780aa81 c3              ret

Examining the contents of the second parameter (0xdc9bf8) shows the following attribute that was returned from the arena belonging to the document. This structure will then be used to update the first parameter.

0:000> !py print(itchi.struc_6a4f0(offset=pykd.expr('poi(@esp+4*1)')).l)
<class itchi.struc_6a4f0> 'unnamed_7b60c28' {unnamed=True}
[dc9bf8] <instance itchi.u32 'v_identifier_0'> 0x32000001 (838860801)
[dc9bfc] <instance c(pstr.wstring<wchar_t<utf-16-le>>) 'v_wDescription?_4'> (16) '\u30b3\u30fc\u30c9\u30da\u30fc\u30b8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
...
[dc9c3c] <instance itchi.u32 'v_important_44'> 0x00000000 (0)
[dc9c40] <instance itchi.u32 'v_someType?_48'> 0x0d0e0a0d (219023885)
[dc9c44] <instance itchi.u32 'v_someStringIndex_4c'> 0x00000000 (0)
[dc9c48] <instance itchi.u32 'v_extra1_50'> 0x000000b8 (184)
...
[dc9da8] <instance dynamic.block(0) 'extra(54)'> ""

Continuing execution, the function call will copy attributes from its second parameter into the fields within the first parameter. Afterwards, it will write the attribute identifier (0x32000001) into an object in the stack before encountering the following call instruction. This function at this call instruction takes two parameters and is used to look through a static array for a matching attribute identifier, as well as a description once that attribute identifier has been found. The second parameter is an object that contains the attributes to match, where the first parameter (1) is the type of match to use when looking for the attribute.

eax=00dc9b78 ebx=00000000 ecx=00000000 edx=00000000 esi=00dc9bf8 edi=06ae9000
eip=2780a765 esp=00dc9b64 ebp=00dc9be0 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
jsvda!Ordinal929+0x22d9:
2780a765 e84ffeffff      call    jsvda!Ordinal929+0x212d (2780a5b9)

0:000> dc @esp L2
00dc9b64  00000001 00dc9b78                    ....x...

Examining the contents of the memory in the second parameter (0xdc9b78) shows that offset +0x44 contains the identifier 0x32000001 (hash value for protected item) being searched for. It should also be noted that at this point, the description at offset +0x4 has not been initialized by the caller.

0:000> !py print(itchi.global_73db8(offset=pykd.expr('poi(@esp+4*1)')).l)
<class itchi.global_73db8> 'unnamed_7b60148' {unnamed=True}
[dc9b78] <instance itchi.u32 'field_0'> 0x30fc30b3 (821833907)
[dc9b7c] <instance c(pstr.wstring<wchar_t<utf-16-le>>) 'v_wDescription_4'> (16) '\u30c9\u30da\u30fc\u30b8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
[dc9b9c] <instance dynamic.array(u32, 8) 'v_wUnusedDescription_24'> itchi.u32[8] "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
[dc9bbc] <instance itchi.u32 'v_identifier?_44'> 0x32000001 (838860801)
[dc9bc0] <instance itchi.u16 'v_key_48'> 0x0000 (0)
[dc9bc2] <instance itchi.u16 'field_4A'> 0x0000 (0)
[dc9bc4] <instance itchi.u32 'v_key_4c'> 0x000000b8 (184)
[dc9bc8] <instance itchi.u32 'v_fieldType_50'> 0x00000000 (0)
[dc9bcc] <instance itchi.u16 'field_54'> 0x0000 (0)
[dc9bce] <instance itchi.u16 'field_56'> 0x0000 (0)
[dc9bd0] <instance itchi.u32 'field_58'> 0x00000000 (0)
[dc9bd4] <instance itchi.u32 'v_fieldSize?_5c'> 0x00000004 (4)
[dc9bd8] <instance itchi.u16 'field_60'> 0x0001 (1)
[dc9bda] <instance itchi.u16 'field_62'> 0x0000 (0)
[dc9bdc] <instance itchi.u32 'v_flags?_64'> 0x00dc9c58 (14457944)

Stepping over this call instruction shows that it returns an error code (0x800f0000) when trying to find a matching attribute.

0:000> p
eax=800f0000 ebx=00000000 ecx=00000000 edx=00000000 esi=00dc9bf8 edi=06ae9000
eip=2780a76a esp=00dc9b64 ebp=00dc9be0 iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200286
jsvda!Ordinal929+0x22de:
2780a76a 59              pop     ecx

0:000> r eax
eax=800f0000

Resuming execution after the match has returned an error results in encountering the instruction. This instruction tests a field at offset +0x44 of the attribute object pointed to by %esi (0xdc9bf8), which originates from the stream in the document.

0:000> g jsvda+6a794
eax=00000006 ebx=00000000 ecx=00dc9c08 edx=05d1f40c esi=00dc9bf8 edi=06ae9000
eip=2780a794 esp=00dc9b60 ebp=00dc9be0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
jsvda!Ordinal929+0x2308:
2780a794 8b4644          mov     eax,dword ptr [esi+44h] ds:0023:00dc9c3c=00000000

0:000> !py print(itchi.struc_6a4f0(offset=pykd.expr('@esi')).l)
<class itchi.struc_6a4f0> 'unnamed_7b60c28' {unnamed=True}
[dc9bf8] <instance itchi.u32 'v_identifier_0'> 0x32000001 (838860801)
[dc9bfc] <instance c(pstr.wstring<wchar_t<utf-16-le>>) 'v_wDescription?_4'> (16) '\u30b3\u30fc\u30c9\u30da\u30fc\u30b8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
...
[dc9c3c] <instance itchi.u32 'v_important_44'> 0x00000000 (0)
[dc9c40] <instance itchi.u32 'v_someType?_48'> 0x0d0e0a0d (219023885)
[dc9c44] <instance itchi.u32 'v_someStringIndex_4c'> 0x00000000 (0)
[dc9c48] <instance itchi.u32 'v_extra1_50'> 0x000000b8 (184)
...
[dc9da8] <instance dynamic.block(0) 'extra(54)'> ""

This instruction checks to see if the field has two of its bits set before taking the branch from the instruction at offset +0x6a7ab (0x2780a7ab) of the library. Due to the field’s value not having these bits set, the function will take an error path to return from the function. This path will fail to copy the attribute identifier in the object (0x6ae9000) that was passed in its parameters.

0:000> u .
jsvda!Ordinal929+0x2308:
2780a794 8b4644          mov     eax,dword ptr [esi+44h]
2780a797 83c40c          add     esp,0Ch
2780a79a 250000000f      and     eax,0F000000h
2780a79f 3d00000004      cmp     eax,4000000h
2780a7a4 7407            je      jsvda!Ordinal929+0x2321 (2780a7ad)
2780a7a6 3d00000002      cmp     eax,2000000h
2780a7ab 7506            jne     jsvda!Ordinal929+0x2327 (2780a7b3)

0:000> ph
eax=00000000 ebx=00000000 ecx=00dc9c08 edx=05d1f40c esi=00dc9bf8 edi=06ae9000
eip=2780a7a4 esp=00dc9b6c ebp=00dc9be0 iopl=0         nv up ei ng nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200287
jsvda!Ordinal929+0x2318:
2780a7a4 7407            je      jsvda!Ordinal929+0x2321 (2780a7ad)      [br=0]

0:000> ph
eax=00000000 ebx=00000000 ecx=00dc9c08 edx=05d1f40c esi=00dc9bf8 edi=06ae9000
eip=2780a7ab esp=00dc9b6c ebp=00dc9be0 iopl=0         nv up ei ng nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200287
jsvda!Ordinal929+0x231f:
2780a7ab 7506            jne     jsvda!Ordinal929+0x2327 (2780a7b3)      [br=1]

0:000> t
eax=00000000 ebx=00000000 ecx=00dc9c08 edx=05d1f40c esi=00dc9bf8 edi=06ae9000
eip=2780a7b3 esp=00dc9b6c ebp=00dc9be0 iopl=0         nv up ei ng nz na pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200287
jsvda!Ordinal929+0x2327:
2780a7b3 bb57000380      mov     ebx,80030057h

0:000> u .
jsvda!Ordinal929+0x2327:
2780a7b3 bb57000380      mov     ebx,80030057h
2780a7b8 5f              pop     edi
2780a7b9 8bc3            mov     eax,ebx
2780a7bb 5e              pop     esi
2780a7bc 5b              pop     ebx
2780a7bd c9              leave
2780a7be c3              ret

If we dump the contents of the object containing the attribute information that is to be used (0x6ae9000), we will see the following layout. In this structure, our attribute description at offset +0x8, and fields at both offset +0x48 and offset +0x4c, are initialized, whereas the identifier at offset +0x0 of the structure is still zero.

0:000> !py print(itchi.object_6b3a6(offset=pykd.expr('@edi')).l)
<class itchi.object_6b3a6> 'unnamed_7bc0c88' {unnamed=True}
[6ae9000] <instance itchi.u32 'v_identifier_0'> 0x00000000 (0)
[6ae9004] <instance itchi.u32 'v_important_4'> 0x00000000 (0)
[6ae9008] <instance c(pstr.wstring<wchar_t<utf-16-le>>) 'v_wDescription?_8'> (16) '\u30b3\u30fc\u30c9\u30da\u30fc\u30b8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
...
[6ae9048] <instance itchi.u32 'v_someType?_48'> 0x0d0e0a0d (219023885)
[6ae904c] <instance itchi.u32 'v_extra1size_4c'> 0x000000b8 (184)
[6ae9050] <instance itchi.u32 'field_50'> 0x00000000 (0)
[6ae9054] <instance itchi.u32 'field_54'> 0x00000000 (0)
[6ae9058] <instance itchi.u32 'field_58'> 0x00000000 (0)
[6ae905c] <instance itchi.u32 'field_5c'> 0x00000000 (0)
[6ae9060] <instance itchi.u32 'field_60'> 0x00000000 (0)

After resuming execution and continuing into the caller, the calling function will then check the field at offset +0x48 of the structure. If this field is not set to one of the required values, the size at offset +0x4c will be used by the allocation at the following call instruction. This function takes two parameters, with the first one being the size and the second being the pointer to store the allocation at. The size (0xb8) is taken from the structure that was returned from a stream in the document, and the destination for the pointer containing the allocated memory comes directly from its parameter. This parameter resides within the scope of the JSTARO25.OCX function, as well as the current function.

0:000> g jsvda+533ff
eax=000000b8 ebx=06ae9000 ecx=06ae9000 edx=05d1f40c esi=00000001 edi=00000000
eip=277f33ff esp=00dc9c60 ebp=00dc9cdc iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200202
jsvda!Ordinal968+0x212d:
277f33ff e8d257fcff      call    jsvda!Ordinal969+0x837 (277b8bd6)

0:000> dc @esp L2
00dc9c60  000000b8 00dc9d2c                    @...,...

0:000> * Verify that the destination parameter is the same as the one in the JSTARO25.OCX library
0:000> ? poi(@esp+4*1) == @$t1
Evaluate expression: 1 = 00000001

As soon as we step over the instruction, we can check the address of the parameter to verify the allocation that was made. If we examine the header of the allocation, we can see that according to the field at offset +0xa it was allocated in the 7th bucket within the application’s allocator.

0:000> p
eax=00000000 ebx=06ae9000 ecx=00000000 edx=00000000 esi=00000001 edi=00000000
eip=277f3404 esp=00dc9c60 ebp=00dc9cdc iopl=0         nv up ei pl zr ac pe cy
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200257
jsvda!Ordinal968+0x2132:
277f3404 8bf0            mov     esi,eax

0:000> dc @$t1 L1
00dc9d2c  06be7760                             `w..

0:000> !py print(itchi.threadSafeHeader(offset=pykd.expr('poi(@$t1)') - 0x20).l)
<class itchi.threadSafeHeader> 'unnamed_7bc0f88' {unnamed=True}
[6be7740] <instance c(ptype.pointer_t<itchi.threadSafeHeader>) 'p_listEntry_0'> *0x0
[6be7744] <instance itchi.u32 'v_count_4'> 0x00000001 (1)
[6be7748] <instance itchi.u16 'vw_flags_8'> 0xc000 (49152)
[6be774a] <instance itchi.u16 'vw_sizeIndex_a'> 0x0007 (7)
[6be774c] <instance itchi.u32 'v_tag_c'> 0x004e4942 (5130562)
[6be7750] <instance itchi.u32 'v_bucketIndex_10'> 0xffffffff (4294967295)
[6be7754] <instance itchi.u32 'pf_release_14'> 0x277b77eb (662403051)
[6be7758] <instance itchi.u32 'field_18'> 0x00000000 (0)
[6be775c] <instance itchi.u32 'v_size_1c'> 0x000000b8 (184)

Resuming execution to the next call, the call instruction at the following address will perform a final search through the arena using the object that was initialized with a missing attribute. This instruction takes 6 parameters, which include the arena descriptor as the first parameter, the structure with the missing attribute as the second, the field from offset +0x4c of the structure as the fourth parameter and the buffer that was just allocated as the fifth parameter. It is probably worth noting that when the library scans an attribute arena for a valid attribute, it will only terminate when the description at offset +0x4 of the object within the file is empty. This allows one to provide a member within the arena that satisfies the requirements of this scan.

0:000> g jsvda+53421
eax=00dc9d2c ebx=06ae9000 ecx=00dc9d2c edx=00000000 esi=00000000 edi=00000000
eip=277f3421 esp=00dc9c50 ebp=00dc9cdc iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200246
jsvda!Ordinal968+0x214f:
277f3421 e8f5e1ffff      call    jsvda!Ordinal968+0x349 (277f161b)

0:000> dc @esp L6
00dc9c50  27831270 06ae9000 00000000 000000b8  p..'............
00dc9c60  06be7760 00dc9ce8                    `w......

0:000> !py print(itchi.object_6b3a6(offset=pykd.expr('poi(@esp+4*1)')).l['v_identifier_0'])
[6ae9000] <instance itchi.u32 'v_identifier_0'> 0x00000000 (0)

0:000> !py print(itchi.object_6b3a6(offset=pykd.expr('poi(@esp+4*1)')).l['v_important_4'])
[6ae9004] <instance itchi.u32 'v_important_4'> 0x00000000 (0)

0:000> !py print(itchi.object_6b3a6(offset=pykd.expr('poi(@esp+4*1)')).l['v_wDescription?_8'])
[6ae9008] <instance c(pstr.wstring<wchar_t<utf-16-le>>) 'v_wDescription?_8'> (16) '\u30b3\u30fc\u30c9\u30da\u30fc\u30b8\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'

0:000> !py print(itchi.object_6b3a6(offset=pykd.expr('poi(@esp+4*1)')).l['v_wDescription?_8'].str())
コードページ

Stepping over this call instruction will result in an error being returned. This error will result in the current function releasing the memory that was just allocated. This is done by the branch at offset +0x5342d of the library (0x277f342d).

0:000> p
eax=800f0000 ebx=06ae9000 ecx=ffffffff edx=00e8f000 esi=00000000 edi=00000000
eip=277f3426 esp=00dc9c50 ebp=00dc9cdc iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200286
jsvda!Ordinal968+0x2154:
277f3426 8bf0            mov     esi,eax

0:000> r eax
eax=800f0000

0:004> u . L4
jsvda!Ordinal968+0x2154:
277f3426 8bf0            mov     esi,eax
277f3428 83c418          add     esp,18h
277f342b 3bf7            cmp     esi,edi
277f342d 0f8c13010000    jl      jsvda!Ordinal968+0x2274 (277f3546)

After the branch has been taken, the function will load the address from its parameter that the allocation was written to into the %edi register. Then it will dereference the pointer and release it.

0:000> g jsvda+53546
eax=800f0000 ebx=06ae9000 ecx=ffffffff edx=00e8f000 esi=800f0000 edi=00000000
eip=277f3546 esp=00dc9c68 ebp=00dc9cdc iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200286
jsvda!Ordinal968+0x2274:
277f3546 8b7d14          mov     edi,dword ptr [ebp+14h] ss:0023:00dc9cf0=00dc9d2c

0:004> u . Lb
jsvda!Ordinal968+0x2274:
277f3546 8b7d14          mov     edi,dword ptr [ebp+14h]
277f3549 ff37            push    dword ptr [edi]
277f354b e8b456fcff      call    jsvda!Ordinal969+0x865 (277b8c04)
277f3550 59              pop     ecx
277f3551 ebde            jmp     jsvda!Ordinal968+0x225f (277f3531)
277f3553 ff74240c        push    dword ptr [esp+0Ch]
277f3557 ff74240c        push    dword ptr [esp+0Ch]
277f355b ff74240c        push    dword ptr [esp+0Ch]
277f355f e8a6d30000      call    jsvda!Ordinal940+0x5439 (2780090a)
277f3564 83c40c          add     esp,0Ch
277f3567 c3              ret

0:000> ph
eax=800f0000 ebx=06ae9000 ecx=ffffffff edx=00e8f000 esi=800f0000 edi=00dc9d2c
eip=277f354b esp=00dc9c64 ebp=00dc9cdc iopl=0         nv up ei ng nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200286
jsvda!Ordinal968+0x2279:
277f354b e8b456fcff      call    jsvda!Ordinal969+0x865 (277b8c04)

0:000> dc @esp L1
00dc9c64  06be7760 

If we examine the header of the allocation, we can see that its reference count is currently set to 1. If we step over the call instruction, the library will release the memory back to the application’s memory manager. We can then examine the header of the allocation and note its reference count has been decremented to 0. Afterwards, the function will return the error code back to the caller.

0:000> !py print(itchi.threadSafeHeader(offset=pykd.expr('poi(@$t1)') - 0x20).l['v_count_4'])
[6be7744] <instance itchi.u32 'v_count_4'> 0x00000001 (1)

0:000> p
jsvda!Ordinal968+0x227e:
277f3550 59              pop     ecx

0:000> u . L2
jsvda!Ordinal968+0x227e:
277f3550 59              pop     ecx
277f3551 ebde            jmp     jsvda!Ordinal968+0x225f (277f3531)

0:000> !py print(itchi.threadSafeHeader(offset=pykd.expr('poi(@$t1)') - 0x20).l)
<class itchi.threadSafeHeader> 'unnamed_986e5f8' {unnamed=True}
[6be7740] <instance c(ptype.pointer_t<itchi.threadSafeHeader>) 'p_listEntry_0'> *0x6be77a4
[6be7744] <instance itchi.u32 'v_count_4'> 0x00000000 (0)
[6be7748] <instance itchi.u16 'vw_flags_8'> 0xc000 (49152)
[6be774a] <instance itchi.u16 'vw_sizeIndex_a'> 0x0007 (7)
[6be774c] <instance itchi.u32 'v_tag_c'> 0x004e4942 (5130562)
[6be7750] <instance itchi.u32 'v_bucketIndex_10'> 0xffffffff (4294967295)
[6be7754] <instance itchi.u32 'pf_release_14'> 0x277b77eb (662403051)
[6be7758] <instance itchi.u32 'field_18'> 0x00000000 (0)
[6be775c] <instance itchi.u32 'v_size_1c'> 0x000000b8 (184)

0:004> u 277f3531 L6
jsvda!Ordinal968+0x225f:
277f3531 8bc6            mov     eax,esi
277f3533 5f              pop     edi
277f3534 5e              pop     esi
277f3535 5b              pop     ebx
277f3536 c9              leave
277f3537 c3              ret

Continuing execution until we return back into the function belonging to the JSTARO25.OCX library, we’ll encounter the following call instruction. Due to the function inside the library allocating its memory directly into a variable belonging to the frame of this function and returning an error, error handling of this function will proceed to check the reference and attempt to also free it. The following call instruction does this by freeing its single parameter.

eax=06be7760 ebx=06bf6580 ecx=070d0db8 edx=00aa5000 esi=00000000 edi=00000051
eip=54ddcf45 esp=00dc9d20 ebp=00dc9d64 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00200206
JSTARO25!DllUnregisterServer+0x34797e:
54ddcf45 e8f64891ff      call    JSTARO25!DllCanUnloadNow+0x934 (546f1840)

0:000> dc @esp L1
00dc9d2c  06be7760                             `w..

0:000> * Verify that the pointer being freed is the same one
0:000> ? poi(@esp+4*0) == @$t1
Evaluate expression: 1 = 00000001

As we step over this call instruction, the memory that was released by the library will be released by the allocator again. This results in the field at offset +0x4 of the allocation’s header being decremented. As a result of this, this can cause memory corruption, which can result in code execution within the context of the application.

0:000> p
JSTARO25!DllUnregisterServer+0x347983:
54ddcf4a 59              pop     ecx

0:000> !py print(itchi.threadSafeHeader(offset=pykd.expr('poi(@$t1)') - 0x20).l['v_count_4'])
[70e51e4] <instance itchi.u32 'v_count_4'> 0xffffffff (4294967295)

Resuming execution from this point can potentially trigger a crash within the application.

0:000> g
(11c0.11c4): C++ EH exception - code e06d7363 (first chance)
(11c0.11c4): Windows Runtime Originate Error - code 40080201 (first chance)
(11c0.11c4): Unknown exception - code e0000001 (first chance)
(11c0.11c4): C++ EH exception - code e06d7363 (first chance)
(11c0.11c4): C++ EH exception - code e06d7363 (first chance)
(11c0.11c4): Windows Runtime Originate Error - code 40080201 (first chance)
(11c0.11c4): Unknown exception - code e0000001 (first chance)
(11c0.11c4): C++ EH exception - code e06d7363 (first chance)
(11c0.11c4): C++ EH exception - code e06d7363 (first chance)
(11c0.11c4): Windows Runtime Originate Error - code 40080201 (first chance)
(11c0.11c4): Unknown exception - code e0000001 (first chance)
(11c0.11c4): C++ EH exception - code e06d7363 (first chance)
(11c0.11c4): C++ EH exception - code e06d7363 (first chance)
(11c0.11c4): 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=07b1f010 ecx=07b1f018 edx=00000000 esi=07b1ef18 edi=01140000
eip=77437196 esp=00d55f68 ebp=00d55f84 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010246
ntdll!RtlpCreateSplitBlock+0x89:
77437196 8b4004          mov     eax,dword ptr [eax+4] ds:0023:00000004=????????

Exploit Proof of Concept

To modify a given document with the necessary attributes, run the proof-of-concept with Python against the desired document using the following parameters:

$ python poc.py3.zip modify /path/to/document.jtd

This will update the document in-place with the necessary changes required to trigger the vulnerability. The same proof-of-concept can be used to process a document and scan it for the necessary attributes by using the “read” command instead of “modify”.

The attributes used by the application and described by this document can be found within the “\4JSRV_SummaryInformation” stream. At the beginning of this stream is a 0x40 byte header.

<class jtd.JSRV_SummaryInformation> 'unnamed_7ff48d0bd9c0' {unnamed=True}
[0] <instance jtd.u32 'sig'> 0x00009030 (36912)  
[4] <instance jtd.sub_277F2074_9030 'header'> "\x40\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe6\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
xe6\x08\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
[40] <instance dynamic.block(0) 'padding(block1)'> ""        
[40] <instance dynamic.blockarray(jtd.struc_6a4f0, 2278) 'block1'> jtd.struc_6a4f0[21] "\x01\x00\x00\x32\xb3\x30\xfc   ..skipped ~2258 bytes..  \x00\x00\x00\x00
\x00\x00"                                                                       
[926] <instance dynamic.block(0) 'padding(block2)'> ""
[926] <instance dynamic.block(0) 'block2'> ""    

This header contains the shape of the contents of the stream and begins with the header size.

<class jtd.sub_277F2074_9030> 'header'                                          
[4] <instance jtd.u32 '_1'> 0x00000040 (64)           
[8] <instance jtd.u32 '2'> 0x00000000 (0)
[c] <instance jtd.u32 '3'> 0x00000000 (0)
[10] <instance jtd.u32 '4'> 0x00000000 (0)       
[14] <instance jtd.u32 'v8_5'> 0x000008e6 (2278)
[18] <instance jtd.u32 '6'> 0x00000000 (0)
[1c] <instance jtd.u32 '7'> 0x00000000 (0)
[20] <instance jtd.u32 'v11_8'> 0x000008e6 (2278)
[24] <instance jtd.u32 'v9_9'> 0x00000000 (0)
[28] <instance jtd.u32 'a'> 0x00000000 (0)                                                                                                                      
[2c] <instance jtd.u32 'b'> 0x00000000 (0)
[30] <instance jtd.u32 'c'> 0x00000000 (0)
[34] <instance jtd.u32 'd'> 0x00000000 (0)
[38] <instance jtd.u32 'e'> 0x00000000 (0)
[3c] <instance jtd.u32 'f'> 0x00000000 (0)

The first attribute that follows the header (according to the size) has the following format, and the fields at offset +0x0, +0x4, +0x44, +0x48 and +0x50 are used to trigger the required error condition. For more details, please review the proof-of-concept.

<class jtd.struc_6a4f0> '0' {uninitialized=True}
[40] <class jtd.u32> v_identifier_0 ???
[44] <class pstr.wstring> v_wDescription?_4 ???
[64] <class jtd.u32> field_24 ???
[68] <class jtd.u32> field_28 ???
[6c] <class jtd.u32> field_2c ???
[70] <class jtd.u32> field_30 ???
[74] <class jtd.u32> field_34 ???
[78] <class jtd.u32> field_38 ???
[7c] <class jtd.u32> field_3c ???
[80] <class jtd.u32> field_40 ???
[84] <class jtd.u32> v_important_44 ???
[88] <class jtd.u32> v_someType?_48 ???
[8c] <class jtd.u32> v_someStringIndex_4c ???
[90] <class jtd.u32> v_extra1_50 ???
[94] <class jtd.u32> v_extra2_54 ???
[98] <class jtd.u32> field_58 ???
[9c] <class jtd.u32> field_5c ???
[a0] <class __extra1> extra1 ???
[a0] <class __padding_extra1> padding(extra1) ???
[a0] <class __extra2> extra2 ???
[a0] <class __padding_extra2> padding(extra2) ???
[a0] <class ptype.block> space ???
TIMELINE

2022-12-19 - Vendor Disclosure
2023-04-04 - Vendor Patch Release
2023-04-05 - Public Release

Credit

Discovered by a member of Cisco Talos.