Exploit-DB #40721
Description: Microsoft Internet Explorer 8/9/10/11 / IIS / CScript.exe/WScript.exe VBScript - CRegExp..Execute Use of Uninitialized Memory (MS14-080/MS14-084)
Type: remote
Platform: windows
Date: 2016-11-07
Author: Skylined

Exploit:
<!--
Source: http://blog.skylined.nl/[PII_REDACTED]1.html

Synopsis

A specially crafted script can cause the VBScript engine to access data before initializing it. An attacker that is able to run such a script in any application that embeds the VBScript engine may be able to control execution flow and execute arbitrary code. This includes all versions of Microsoft Internet Explorer.

Known affected versions, attack vectors and mitigations

vbscript.dll
The issue affects versions 5.6 through 5.8 and both the 32- and 64-bit vbscript.dll binaries.

Windows Script Host
VBScript can be executed in the command line using cscript.exe/wscript.exe. An attacker would need to find a script running on a target machine that accepts an attacker supplied regular expression and a string, or be able to execute his/her own script. However, since the later should already provide an attacker with arbitrary code execution, no additional privileges are gained by exploiting this vuln.

Microsoft Internet Explorer
VBScript can be executed from a web-page; MSIE 8, 9, 10 and 11 were tested and are all affected. MSIE 11 requires a META tag to force it to render the page as an earlier version, as MSIE 11 attempts to deprecate vbscript (but fails, so why bother?). An attacker would need to get a target user to open a specially crafted web-page. Disabling scripting, particularly VBScript, should prevent an attacker from triggering the vulnerable code path. Enabling Enhanced Protected Mode appears to disable VBScript on my systems, but I have been unable to find documentation on-line that confirms this is by design.

Internet Information Server (IIS)
If Active Server Pages (ASP) are enabled, VBScript can be executed in Active Server Pages. An attacker would need to find an asp page that accepts an attacker supplied regular expression and a string, or be able to inject VBScript into an ASP page in order to trigger the vulnerability.
Repro
Below are three repro files that trigger the issue in Windows Script Host (repro.vbs), Microsoft Internet Explorer (repro.html), and Internet Information Server (repro.asp).

Repro.vbs:

Set oRegExp = New RegExp
oRegExp.Pattern = "A|()*?$"
oRegExp.Global = True
oRegExp.Execute(String(&H11, "A") & "x")

Repro.html:

<html>
  <head>
    <meta http-equiv="X-UA-Compatible" content="IE=10">
    <script language="VBScript">
      Set oRegExp = New RegExp
      oRegExp.Pattern = "A|()*?$"
      oRegExp.Global = True
      oRegExp.Execute(String(&H11, "A") & "x")
    </script>
  </head>
</html>

Repro.asp:

<%
Set oRegExp = New RegExp
oRegExp.Pattern = "A|()*?$"
oRegExp.Global = True
oRegExp.Execute(String(&H11, "A") & "x")
%>


Description

During normal operation, when you execute the RegExp.Execute method from VBScript the code in vbscript.dll executes the CRegExp::Execute function. This function creates a CMatch object for each match found, and stores pointers for all of these CMatch objects in a singly linked list of CMatchBlock structures (Note: the vbscript.dll symbols do not provide a name for this structure, so I gave it this name). Each CMatchBlock structure can store up to 16 such pointers, as well as a pointer to the next CMatchBlock. This last pointer is NULL unless all pointers in the CMatchBlock object are in use and more storage is needed, in which case a new CMatchBlock object is created and a link to the new object is added to the last one in the list. The code counts how many matches it has found so far, and this corresponds to the number of CMatch objects it has allocated.

The following pseudo-code represents these two structures:

CMatchBlock {
  00 04 CMatchBlock* poNextCMatchBlock
  04 40 CMatch* apoCMatches[16]
} // size = 0x44 (x86) or 0x88 (x64)

CMatch {
  00 0C void** apapVFTables[3]              
  0C 04 DWORD dwUnknown_0C
  10 04 DWORD poUnknownObject_10
  14 04 DWORD poUnknownObject_14
  18 04 DWORD poUnknownObject_18
  1C 04 DWORD poUnknownObject_1C
  20 04 DWORD dwUnknown_20
  24 04 BSTR sValue
  28 04 INT[]* paiMatchStartAndEndIndices
  2C 04 INT iCountMatchAndSubMatches
} // size = 0x30 (x86) or unknown (x64)
When an error occurs in this part of the code, the error handling code will try to clean up and free all CMatchBlock structures created before the error occurred. To do this, it walks the linked list of CMatchBlock structures and for each structure, release each CMatch object in the structure. All CMatchBlock structures except the last one should have 16 such pointers, the last CMatchBlock structure can have 1-16, depending on how many matches where found in total. This appears to have been designed to count how many CMatch objects it has yet to free. This counter is initialized to the number of matches found before the error occurred and should be decremented whenever the code frees a CMatch object, so the code can determine how many CMatch object are in the last CMatchBlock structure. However, this code neglects to decrement this counter. This causes the code to assume all CMatchBlock structures have 16 CMatch object pointers if there were more than 16 matches in total, and attempt to release 16 CMatch objects from the last CMatchBlock structure, even if less than 16 pointers to CMatch objects were stored there.

The below pseudo-code represents how the real code works:

poCMatchBlock = poFirstCMatchBlock;
do {
    if (iTotalMatchesCount < 0x10) { // Note 1
        iMatchesInCMatchBlock = iTotalMatchesCount;
    } else {
        iMatchesInCMatchBlock = 0x10; // Note 2
    }
    for (iIndex = 0; iIndex < iMatchesInCMatchBlock; iIndex++) {
        poCMatchBlock->apoCMatches[iIndex].Release(); // Note 3
    }
    poOldCMatchBlock = poCMatchBlock;
    poCMatchBlock = poCMatchBlock->poNextCMatchBlock;
    delete poOldCMatchBlock;
    // Note 4
} while (poCMatchBlock);

For example: if the code finds 17 matches before an error is triggered, 2 CMatchBlock structure