We discovered a high-risk Internet Explorer (IE) vulnerability in the wild on July 11, just a day after Microsoft’s July Patch Tuesday. We immediately sent Microsoft the details to help fix this flaw. While this vulnerability, now designated as CVE-2018-8373, affects the VBScript engine in the latest versions of Windows, Internet Explorer 11 is not vulnerable since VBScript in Windows 10 Redstone 3 (RS3) has been effectively disabled by default.
We discovered the exploit in malicious web traffic. The URL is shown as below:
Figure 1. Malicious URL used
We found this exploit using heuristics, which led to a more in-depth analysis. Interestingly, we found that this exploit sample uses the same obfuscation technique as exploits for CVE-2018-8174, a VBScript engine remote code execution vulnerability patched back in May.
Figure 2. Comparison of CVE-2018-8373 (left side) and CVE-2018-8174 (right side)
Moreover, here’s the sample exploit’s method for running shellcode:
Figure 3. Comparison of how shellcode is run by CVE-2018-8373 (left side) and CVE-2018-8174 (right side)
We suspect that this exploit sample came from the same creator. Our analysis revealed that it used a new use-after-free (UAF) vulnerability in vbscript.dll.
Vulnerability Root Cause Analysis
We now detail our analysis of this vulnerability, which has been addressed in Microsoft’s August Patch Tuesday. Because the original exploit was heavily obfuscated, we demonstrate a proof of concept (PoC) to explain how this vulnerability can be exploited:
Figure 4. IE vulnerability PoC
This PoC defines one class called MyClass, which has one member variable called array and two member functions called Class_Initialize and Default Property Get P. Class_Initialize is a deprecated method, now replaced by the New procedure. It is automatically invoked when an object is first instantiated. In this PoC, the Class_Initialize function is overloaded, and when a call to VBScriptClass::InitializeClass is made, it gets dispatched to the overloaded function instead.
A default property is one class property that can be accessed without specifying it. In this PoC, the Default Property Get function overloads MyClass’s default property. When a call is made to access cls, it is dispatched to the overloaded function instead.
The vulnerability’s trigger flow can be simplified as the following three steps:
1. Set cls = New MyClass
This will call overloaded function Class_Initialize. In Class_Initialize, ReDim array(2) will call vbscript!RedimPreserveArray to create an array whose element’s count is 3:
Figure 5. ReDim array(2) in memory
It will call vbscript!AccessArray to get the address of the array’s element. In vbscript!AccessArray, it will first check if the index of the array’s element is out of boundary:
Figure 6. Check element index in vbscript!AccessArray
Then the element’s address is calculated, gets saved on the stack, and returns the following:
Figure 7. Save element address on the stack
This will call vbscript!AssignVar to set MyClass default property value to cls.array(2). When getting the MyClass default property value, Public Default Property Get P is called and executes the script ReDim array(1) in Public Default Property Get P, which will cause original array.pvData to be freed:
Figure 8. Original pvData is freed
But the address of array(2) is still saved on the stack as described in step two. Public Default Property Get P’s return value will access the freed memory and consequently trigger the use-after-free (UAF) vulnerability in vbscript!AssignVar:
Figure 9. Crash in vbscript!AssignVar
As previously mentioned, vbscript!AccessArray checks if the index of the array’s element is out of boundary. But when getting a default property value of the class, it will trigger the script callback function Default Property Get to modify the array’s length, and not check the array’s element again when accessed in vbscript!AssignVar.
The exploitation can be simplified with the following three steps:
- Use the vulnerability to modify a two-dimensional array’s length to 0x0FFFFFFF.
- Implement Read/Write primitives.
- Fake CONTEXT structure and execute shellcode.
Let’s talk about the exploitation in detail:
1. Modify a two-dimensional array’s length
First, the vulnerability defines two arrays, which we labeled in the following figures as array1 and array2. The array1 is the array previously described in the PoC, and array2 is a two-dimensional array in which each element’s value is 3.
Figure 10. Definition of array2
Then it uses script callback function Default Property Get to free original array1.pvData and set array2 to new array1.pvData. Because the size of the original array1.pvData, which is 0x30 Bytes in memory, is the same as array2.SAFEARRAY structure, some of the array2.SAFEARRAY structure will reuse the memory freed in original array1.pvData. Meanwhile, Default Property Get’s return value 0x0FFFFFFFF will cover the structure SAFEARRAYBOUND of array2.SAFEARRAY and modify the two-dimensional array’s length to 0x0FFFFFFF.
Figure 11. Definition of Default Property Get
Figure 12. Steps for modifying array’s length
2. RW primitives
Here it gets one array, array1(index_vuln)(0x0FFFFFFE, 2), whose length is tempered by UAF. By searching array1’s element, the index_vuln can be found in the following script:
Figure 13. Searching array1(index_vuln)(0x0FFFFFFE, 2)
Then it uses array1(index_vuln)(0x0FFFFFFE, 2) to realize out-of-bounds (OOB) and find two array’s elements for type confusion.
Figure 14 and 15. Searching two array’s elements
Here the vulnerability gets two array’s elements: array1(index_B)(0, 0) and array1(index_vuln)(index_A, 0), which have a distance of 8 Bytes in memory. The full exploitation of searching in memory is shown below:
Figure 16. Demonstration of the way it searches in memory
Finally, it uses the two array’s elements to implement read and write primitives by type confusion:
Figure 17. Implementation of RW Primitives
3. Run shellcode
It uses read primitives to leak modules’ addresses:
Figure 18. Leak modules’ addresses
By modifying some VARIANT’s VarType to 0x4d and value to 0, vbscript!VAR::Clear will be called, then the call stack return address will be modified to NtContinue’s address and faked CONTEXT structure to run shellcode:
Figure 19. Modify VARIANT
Figure 20. Run shellcode
Based on our analysis, this vulnerability can be steadily exploited. Moreover, since it is the second VB engine exploit found in the wild this year, it is not far-fetched to expect other vulnerability findings in the VB engine in the future.
Mitigation and Solutions
As a first line of defense, we recommend applying the latest security patches once they’re available to prevent exploits. Users can also employ solutions that defend against possible exploits. A proactive, multilayered approach to security is key against threats that exploit vulnerabilities — from the gateway, endpoints, networks, and servers. Trend Micro™ OfficeScan™ with XGen™ endpoint security has Vulnerability Protection that shields endpoints from identified and unknown vulnerability exploits even before patches are deployed. Trend Micro’s endpoint solutions such as Trend Micro™ Smart Protection Suites and Worry-Free™ Business Security protect end users and businesses from these threats by detecting and blocking malicious files and all related malicious URLs. The Trend Micro™ Deep Discovery™ solution provides detection, in-depth analysis, and proactive response to today’s stealthy malware and targeted attacks in real time. It provides a comprehensive defense tailored to protect organizations against targeted attacks and advanced threats through specialized engines, custom sandboxing, and seamless correlation across the entire attack life cycle.
- 1009218 – Microsoft Windows VBScript Engine Use-After-Free Vulnerability
- 32721: Microsoft VBScript Engine Sub Default Property Use-After-Free Vulnerability
Indicator of Compromise (IoC)
Related hash (SHA256):
- 0d6fe137790e2ebdf4fac2dd500656f3a6f74c0d1598251929ea3558f965675f – detected as HTML_EXPLOIT.YYRV