Analysis of CVE-2018-8174 Vulnerability

AhnLab ASEC performed an analysis on IE vulnerability CVE-2018-8174 which is being widely used to distribute ransomware and Korean malware. This vulnerability is used to distribute Magniber ransomware as well, and users must apply security patch to prevent damage that can be done.

MS security update page (CVE-2018-8174)

– https://portal.msrc.microsoft.com/ko-kr/security-guidance/advisory/CVE-2018-8174

01. Summary

1) CVE-2018-8174 overview

CVE-2018-8174 vulnerability is created as a result of object reuse that occurs when Use After Free vulnerability of VBScript engine surfaces.

This vulnerability allows remote execution, and the affected versions are: Internet explorer 8, Internet explorer 9, Internet explorer 10, Internet explorer 11 (1803 or older version), Windows 10 (1803 or older), Windows 7, Windows 8, and Windows Server.

02. Background Knowledge

1) How VBScript engine runs a script

a) Definition

VBScript engine converts script to P-Code (precompiled code) for analysis, then runs it. Converted P-Code contains 112 values (0x00 – 0x6F) and RunNoEH – VBScript engine’s inner function – analyzes it and calls VBScript function that fits the P-Code. Figure 1 shows the RunNoEH function.

Figure 1. Definition of RunNoEH

b) Execution method

P-Code runs in RunNoEH function, and the code is located within CScriptRuntime class, the first parameter of Figure 1. Figure 2 shows CScriptRuntime class and the location of P-Code can be found via the member variable named Compiled Script that is located at 0xC0. Member variable named Position Counter that is located at 0xB4 of Figure 2 is pointing at the P-Code command to run next. This performs similar role as EIP register.

Figure 2. CScriptRuntime class

In VBScript engine, P-Code is run with Call-Return method as shown in Figure 3. In the Call-Return method, when Global Code’s P-Code runs and meets P-Code that calls a call internally, the P-Code that is run next is saved and the P-Code command of the function is run. 

In VBScript engine, P-Code is run with Call-Return method as shown in Figure 3. In the Call-Return method, when Global Code’s P-Code runs and meets P-Code that calls a call internally, the P-Code that is run next is saved and the P-Code command of the function is run. 

Figure 3. Execution flow of P-Code

c) Tools

Kaspersky Lab published a P-Code extraction script in github (Reference 1). This script can be used to extract Compiled Script of CScriptRuntime class mentioned above and use it for analysis.

03. Detailed analysis

1) CVE-2018-8174

a) Principle of vulnerability occurrence

CVE-2018-8174 vulnerability is a name for the object reuse issue that occurs when Use After Free vulnerability of VBScript engine surfaces.

Figure 4 and Figure 5 below is an overview of principle behind the occurrence of Use After Free vulnerability in CVE-2018-8174 vulnerability. The script below is the PoC (Proof of Concept) script of CVE-2018-1874. The occurrence follows the following steps in number order, and the process in Figure 4 is continued in Figure 5.

Figure 4. Principle of Use After Free – 1

Among the steps of the script in Figure 4, Dim of step 1 is declaration of variable. In here, Dim declares global variable o. Redim of step 2 means declaration of array, and two arrays named arr are declared as array. Through step 3, it allocates class object cla1 to arr(0) which is the first declared array. As a result, address of the class object cla1 is saved to the first array arr(0), and reference count for class object becomes 1.

Figure 5. Principle of Use After Free – 2

After the steps in Figure 4 are carried out, the memory deallocation process in step 4 of Figure 5 is proceeded. The script then attempts to delete arr, which is the array declared by Erase statement. When deleting arr(0) which is the first element of arr array, the Class_Terminate function of class cla1 is called as a callback function and it proceeds with step 5. Process at step 5 saves the address of class object cla1 that has been saved to arr(0) to global variable o declared in step 1. Since the variable that refers to self has increased, the reference count of class cla1 increases by 1. In step 6, the address of class object saved to arr(0) is changed to a different value, and due to this, the reference count of class cla1 decreases by 1. And in step 7, the callback function is terminated, and because the increase and decrease of reference count is consistent, free() function for class object address is called. When the value of global variable o is printed via final step 8, it is pointing at the deallocated memory address.

Figure 6. VBScriptClass::Release function

Memory deallocation is proceeded via function of Figure 6 that has been defined to VBScript.dll. As shown in the code, if reference count is 0 after calling InterlockedDecrement, VBScriptClass::TerminateClass function is called, and if the reference count of class (v1 variable) for memory deallocation is 1, free() function is called. Upon calling Class_Terminate callback function, the script saves the address value of the class object before deallocation of global variable o within the callback function, and calls free () function by balancing the reference number. Following this, the real memory is deallocated and the address value can be used as Dangling pointer.

b) Use After Free The principle of Use After Free occurrence will be explained thoroughly using the code of Figure 7.

Figure 7. Script of Use After Free

Redim in step 1 of Figure 7 internally calls functions (See Figure 8).

Figure 8. Internal call of Redim

When up to SafeArrayAllocData function is called, tagSAFEARRAY struct is created. The structure of tagSAFEARRAY struct is shown in Figure 9.

Figure 9. tagSAFEARRAY struct

Figure 10. Allocated memory value

Figure 11 Variant structure

By allocating to redim step 1 of Figure 7, reset memory area is created, and the value is allocated via Set command. Figure 10 shows the value that was allocated via this process. The form of value that is saved is Variant struct (Reference 4). Variant struct is a struct that transmits various types of data, and the valid data type is decided via VT (Variant Type), which is the first member (Reference 5). As shown in Figure 11, the current allocated VT value of Variant struct is 0x09 (VT_DISPATCH).

Figure 12. VBScriptClass object

Pointer of VT_DISPATCH is saved in VT value of Variant struct, and 0x021C6AB8 is saved. The value saved into this address is a space where object info of cla1 that has been allocated to VBScriptClass is saved. Through Set, reference value for cla1 increases, and the reference count has become 2.

Figure 13. Internal call of VbsErase

Erase in step 1 of Figure 7 internally calls functions such as the ones shown in Figure 13.

oleaut32!ReleaseResources function circulates FreedObjectArray array and resets saved values. oleaut32!VariantClear function is a function that resets Variant struct. VariantClear function’s clearing routine changes depending on the VT value of Variant struct. Since VT value of Variant struct is DISPATCH, VBScriptClass::TerminateClass is called. The time Erase function of Figure 7 is called is when the deallocation happened immediately after memory allocation. Thus, reference count becomes 1 as shown in Figure 14, TerminateClass is called, and through reset function, the callback function part in no. 2 of Figure 7 takes place.

Figure 14. Reference count after calling Erase function

The reference count of cla1 array that was reset is 4, and Set part in the highlight 2 of Figure 7 allocates FreedObjectArray array that has object address info to UafArrayA and increases reference count by 1. The address value saved to UafArrayA is used as a Dangling pointer (Figure 15).

Figure 15. Dangling pointer

Figure 16. Change in memory after FreedObjectArray(1)

Afterward, the script allocates a new value to FreedObjectArray(1) array declared by Redim and AssignVar is called internally by vbscript. At this instance, 1 is 0x02(VT_I2) among the VT values of the Variant struct, and the previous 0x09 (VT_Dispatch) value is allocated to 0x02 (VT_I2) as shown in Figure 16. Due to object referencing on FreedObjectArray(1) array, the reference count of cla1 decreases by 1. Callback function call ends while the value of the callback function is adjusted to 4 which is the starting value, and the reference count reaches 0, deallocating memory.

Ultimately, Use After Free occurs because the reference count of the class struct is not validated properly. Dangling pointer that occurs from this is saved to UafArrayA array, and through step 3 of Figure 7., is relocated to ReuseClass and is used in TypeConfusion vulnerability.

c) TypeConfusion

TypeConfusion means causing confusion for the declared Type. Assuming that there is an assigned Type A data as shown in Figure 17, when ShellCode is used to overwrite the area of the assigned data, the data type is changed by the overwriting ShellCode. Upon reading the area of modified Type A, it is recognized as modified Type B instead of Type A. This is the principle of TypeConfusion.

Figure 17. Principle of TypeConfusion

Use After Free is proceeded 2 times in CVE-2018-8174 vulnerability, 2 Dangling Pointers occur, and allocation to ReuseClass occurs 2 times. Since there is no difference in method and principle for Use After Free, only 1 sample was explained. TypeConfusion however, 2 methods are each used differently. The first method will be explained below, and the difference will be explained later.

Figure 18 shows TypeConfusion part of CVE-2018-8174 code. It is a code that forms a memory for TypeConfusion in the address resueObjectA_arr of ReuseClass that has been set with the Dangling pointer which was obtained via Use After Free, step 3 of Figure 7. When SetProp function of resueObjectA_arr class is called, it performs default property declared to ReuseClass. Q that is configured within the default property function is 0x0C, 0x20 value, therefore VT value of Variant struct becomes 0x200C, and the variable form is VT_ARRAY, VT_VARIANT. When 0 is inputted in UafArrayA, VBScript Terminate Class is called, reference count becomes 0, and ReuseClass is freed. Afterward, FakeReuseClass object is assigned to objectImitatingArray. At this instance, address that is same as ReuseClass object address is assigned.

Figure 18. TypeConfusion Script-1

Assigned FakeReuseClass object is shown in Figure 19 below. The upper section of Figure 19 shows the area where ReuseClass object is assigned, and the lower section shows the area where FakeReuseClass is assigned. The white box shows function name and variable name. The area after the white box is the assigned data. Function starts with 0x4C value, and variable declared as dim starts with 0x00.

FakeReuseClass object is assigned to the area where ReuseClass of Figure 19 is assigned. For the first function name, 10 strings are added from p, resulting in the expansion by 16-byte, and SPP function is assigned later.

Figure 19. Memory value before and after TypeConfusion

Memory of objectImitatingArray that has been assigned as FakeReuseClass object is overwritten with pre-configured FakeArrayString. FakeArrayString is a value (0x08,BSTR) composed of String. It is configured to create tagSAFEARRAY of abnormal size. Used value FakeArrayString is BSTR, therefore VT value of mem is changed to 0x08. 0xFFFF, 0x7FFF value is among them, and these values have been configured to be used in TypeConfusion to set the size of SafeArray array to 0x7FFFFFFF. There are also 0x0001, 0x0880, and 0x0001 value. These values are respectively set as the first, second, and the third argument of tagSAFEARRAY struct, and each becomes the size value of the array’s dimension info, array feature info, and array element. The script overwrites mem variable of objectImitatingArray with the configured values above. Upon overwriting, the structure of tagSAFEARRAY is changed as shown in Figure 20. tagSAFEARRAY now has 0x7FFFFFFF arrays with the 0x880( FADF_VARIANT | FADF_HAVEVARTYPE ) property of 1-dimensional array and the array element size of 1. mem that is defined after default property ends and going back to SetProp overwrites mem of ReuseClass using the configured Q value, and the type changes from 0x08(BSTR) to 0x200C(ARRAY, VARIANT). Afterward, when mem of reuseObjectA_arr is called, array with 0x7FFFFFFF size becomes available for use. This array with abnormal size can access the memory of the same size via heap area of the iexplore.exe where the script is loaded.

Figure 20. reuseObjectA_arr memory after TypeConfusion

TypeConfusion is carried out in the same way as resueObjectA_arr for resueObjectB_int. The difference between the description above is P value that’s been configured within default property function and pre-configured FakeArrayString value. VT value of the P value configured inside the default property function is changed from BSTR(0x08) to LONG(0x03), and is filled with empty 16-byte. mem of reuseObjectB_int of empty space and global variable address is inserted to some_memory. The empty value is the heap space of iexplore.exe where script is loaded, and is later used to save certain space of memory.

Figure 21. TypeConfusion Script-2

Script 22. reuseObjectB_int memory after TypeConfusion

d) LeakVBAddr

In the LeakVBAddr part, the array (resueObjectA_arr) made with TypeConfusion earlier and the heap address (resueObjectB_int) is used to obtain the base address of VBScript.dll.

Figure 23. LeakVBAddr script

Figure 24. The changes in memory in LeakVBAddr step

With EmptySub assigned to the emptySub_addr_placeholder variable, object that includes internal address of vbscript.dll is saved. The string that assigned the empty function to a variable is grammatically incorrect, but routine can be performed via On Error Resume Next. Afterward, null is assigned and VT value is changed from Func(0x4C) to NULL(0x01). After assigning EmptySub then assigning Null, VT is edited from Null(VT=0x01) to LONG(0x03), memory address is printed as variable, and memory is leaked. The attacker can use the array (reuseObjectA_arr.mem) of 0x7FFFFFFF size obtained via TypeConfusion function to access heap Address (some_memory) and freely change the type.

e) Execute ShellCode

Figure 25. Code execution script

The execution of ShellCode begins as 0x4D is assigned. Upon assigning 0x4D value, AssignVar is called and VT value of Variant struct is checked. Afterward, branching takes place for the matching value. When the VT value is 0x4D, VAR::Clear is called and ShellCode is run via NtContinue and VirtualProtect due to configured memory.

Figure 26. Code execution process

Figure 27 shows VirtualProtect which is a script that configures memory to give execution privilege to ShellCode. VirtualProtect is called to IP address, download size, and file protection option. 64(0x40) is used as the execution privilege, and this is the PAGE_EXECUTE_READWRITE privilege.

Figure 27. Script for VirtualProtect call parameter memory configuration

Figure 28 shows the script that configures CONTEXT struct which is used to call NtContinue. Registry location is filled with padding value, and memory is configured by using VirtualProtect address value and ShellCode EP address value in the location of EIP, ESP value respectively.

Figure 28. CONTEXT struct configuration script

As explained above, when 0x4D value is assigned to VT, it is called after AssignVar as Var::Clear, and performs the branching statement from Var::Clear (See Figure 29). In loc_6E50089C of Figure 29, the first line is the location (esi+8) of data of Variant struct, and it points at the memory configured with StructForNtContinue of Figure 28. Afterward, it pushes and calls the address of memory that is configured with StructForNtContinue to prompt it to run NtContinue.

As explained above, when 0x4D value is assigned to VT, it is called after AssignVar as Var::Clear, and performs the branching statement from Var::Clear (See Figure 29). In loc_6E50089C of Figure 29, the first line is the location (esi+8) of data of Variant struct, and it points at the memory configured with StructForNtContinue of Figure 28. Afterward, it pushes and calls the address of memory that is configured with StructForNtContinue to prompt it to run NtContinue.

Figure 29. Execution of configured memory

When NtContinue is called, the CONTEXT struct address that was configured as above is called along and this results in VirtualProtect function being called.

VirtualProtect gives execution privilege after receiving ShellCode address due to the configured CONTEXT struct, returns it, and branches to ShellCode which results in the execution of ShellCode.

Figure 30. Execution of ShellCode

Figure 31. Change in memory execution privilege

f) Vulnerability patch

Figure 32. VariantClear before and after CVE-2018-8174 vulnerability patch

Use After Free vulnerability of CVE-2018-8174 underwent oleaut32.dll module patch (Reference 6). Figure 32 shows VariantClear function before and after oleaut32.dll patch. The left side of the figure shows the function before the patch and the right side shows the function after the patch. The function that is called when deallocating assigned Variant struct is shown in Figure 13. VbsErase of vbscript.dll is called and VariantClear function of oleaut32.dll module is called via internal call. Variant struct is deallocated via TerminateClass of vbscript.dll in VariantClear function, and as shown in the right side of Figure 32, the patch made a change so that when it is deallocated, the Variant Type value of the function is filled with 0x00(VT_EMPTY) and deallocated. The VT value of the memory value before the patch was 0x09(VT_DISPATCH), but it changed to 0x00(VT_EMPTY) after the patch. After the patch, using Dangling pointer obtained from Use After Free results in occurrence of error during the phase of copying object address to UafArrayA, rendering it impossible to reuse object.

Categories:Malware Information

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments