Bypassing ASLR with CVE-2015-0071: An Out-of-Bounds Read Vulnerability

Almost every Patch Tuesday cycle contains one bulletin that (for convenience) rolls up multiple Internet Explorer vulnerabilities into a single bulletin. February’s Patch Tuesday cumulative IE bulletin (MS15-009) included a fix for a particularly interesting vulnerability that could be used to bypass one of the key anti-exploit technologies in use today, address space layout randomization (ASLR).

This vulnerability was designated CVE-2015-0071. To be used in an attack, this vulnerability must be combined with another one that is capable of actually running code on the affected machines. In attacks seen by iSIGHT, this has been paired with an Adobe Flash vulnerability (CVE-2014-9163), which was fixed in December.

This vulnerability was found in the jscript9.dll module. To analyze this vulnerability, I examined this file (version 9.0.8112.1645) on a Windows 7, 32-bit system.

Patch differences

Examining the patched and unpatched versions of this DLL, we found a modification im the SetProperty function.

Figure 1. Patched SetProperty function

Figure 2. Unpatched SetProperty function

In the patched version, the function Js::JavascriptRegExpConstructors::EnsureValues is called, and only then is the property’s value set. The unpatched version does not call this particular function at all.

The function EnsureValues can show us how to fully analyze this vulnerability. To do this, we need to explain some data structures dealing with regular expressions.

Related data structures

This page from the MSDN Library explains data structures that store the results of regular expressions. When the JavaScript syntax is used, it has the following properties:

Figure 3. Expression properties

We will only focus on the following properties, as they are relevant for this analysis:

  • Input – the source string
  • lastMatch – the match string
  • $1-$9 – the sub-match string.

How does Internet Explorer implement this? It does so in the DLL file jscript.dll. It uses the following structure named Js::JavascriptRegExpConstructor, which represents the global object RegExp which is used to store a regular expression’s matching results.

  • 0x24: pInputString (the source string)
  • 0x28: startIndex (beginning of the lastMatch string in the pInputString’s index)
  • 0x2c: length (the lastMatch string length)
  • 0x30: isNeedUpdate (whether or not to update the related field)
  • 0x40: pUnmatchString
  • 0x44: pUnSearchString
  • 0x48: pLastMatchString (the address of the matching string)
  • 0x4c: $1 (the submatch 1 string)

All of the strings above have the following structure (named Js::SingleCharString):

  • 0x00: pVtable
  • 0x08: length (the string length)
  • 0x0c: pString (pointer to the string array’s starting address)Demonstration

Let’s consider the following sample code (the test string is one frequently used by Microsoft in demonstrating regular expressions):

var src = "Please send mail to george@contoso.com and someone@example.com. Thanks!";
pattern = “(george)”;
var re = new RegExp( pattern );
re.exec( src );
alert(RegExp.$1);

This is the contents of the memory after re.exec has finished running:

Figure 4. Memory contents after re.exec command

pInputString points to the string “Please send mail…”. lastMatch.startIndex is 0x14, and lastMatch.length is 0x06. isNeedUpdate is True. The other fields still have their initial value. For example, $1 is set to the null string. Only the three fields noted above were modified.

After alert(RegExp.$1) has run, the contents of the memory are as follows:

Figure 5. Memory contents after alert(RegExp.$1)

When we reference RegExp.$1, it will call GetProperty like so:

Figure 6. GetProperty command

To return the proper value, GetProperty calls EnsureValues to update fields related to JavascriptRegExpConstructor. We can see that isNeedUpdate is set false, and the $1-$9 fields are set.

Let’s look in some detail at EnsureValues. This function is quite complex, so we just consider the parts related to this vulnerability:

  1. It checks if isNeedUpdate is false. If yes, the update is finished, so the function will return. Otherwise, it will go to step #2 to update the file.
  2. EnsureValues use the pInputString, beginIndex, length and re.exec results to compute the other fields of the JavascriptRegExpConstructor

With RegExp.$1 as an example, let’s look at how EnsureValues computes for $1.

  1. EnsureValues first uses UnifiedRegex::RegexPattern::GetGroup to get $1-related information. It return startIndex, length, and startIndex. Representation $1 begin address in the pInputString, length is the $1 length.
  2. Call Js::SubString::New to create Js::SingleCharString. The parameters of this are pInputString, startIndex, and length. The return of this function is $1_address.
  3. Set the $1_address at JavascriptRegExpConstructor related field. Here, it is 0x4c.

Now that we have understanding of EnsureValues, we can see how the vulnerability is triggered, and how Microsoft patched it.

Triggering the vulnerability

Consider the following code:


var src = "Please send mail to george@contoso.com and someone@example.com. Thanks!";
pattern = “(george)”;
var re = new RegExp( pattern );
re.exec( src );
RegExp.input = "123456"
alert(RegExp.$1);

Before we run alert(RegExp.$1), if we add a statement before it (the bolded code), jscript9.dll will call Js::JavascriptRegExpConstructor::SetProperty. In the unpatched version, it only change the pinputString field of the global RegExp object.

Next we reference RegExp.$1. Because isneedUpdate is true, the Js::JavascriptRegExpConstructor::GetProperty function will call EnsureValues to compute the rest fields.

Because the inputString is changed to (errorinputString), this will be used to compute the related fields.

If the value of $1.startIndex + $1.length(UnifiedRegex::RegexPattern::GetGroup) is bigger than the inputString, we can use the value of $1 to trigger an out of bounds read, bypassing ASLR.

Root cause of the vulnerability

  1. When regexp.exec finishes, the function just stores the inputString (we call this right_inputString) and the lastmatch string’s index and length.
  2. When we set RegExp.input, RegExp just modified the pInputString field (we call this error_inputString), and didn’t modify the other fields.
  3. When we use RegExp.$1 and read the property, RegExp will use the inputString(error_inputString) field to compute the other fields.
  4. If the error_inputString length is smaller than the $1.startIndex+$1.length, when we reference RegExp.$1, it can read error_inputString out of bounds.

The cause of this vulnerability may be as simple as developers forgetting to call EnsureValues in the SetProperty function.

Solutions and Recommendations

While this vulnerability could pose a risk used with other vulnerabilities, it’s worth noting that Microsoft has already patched these issues as part of their regular Patch Tuesday cycles. Users with up-to-date systems would not be at risk, which highlights the importance of keeping a system’s software up to date.

Post from: Trendlabs Security Intelligence Blog – by Trend Micro

Bypassing ASLR with CVE-2015-0071: An Out-of-Bounds Read Vulnerability

Read more: Bypassing ASLR with CVE-2015-0071: An Out-of-Bounds Read Vulnerability

Story added 13. March 2015, content source with full text you can find at link above.