Last June, I disclosed a use-after-free (UAF) vulnerability in Internet Explorer (IE) to Microsoft. It was rated as critical, designated as CVE-2019-1208, and then addressed in Microsoft’s September Patch Tuesday. I discovered this flaw through BinDiff (a binary code analysis tool) and wrote a proof of concept (PoC) showing how it can be fully and consistently exploited in Windows 10 RS5.

A more in-depth analysis of this vulnerability is in this technical brief. Here’s an overview of the research.

What is CVE-2019-1208 about?

As mentioned, CVE-2019-1208 is a UAF vulnerability. This class of security flaws can corrupt valid data, crash a process, and, depending on when it is triggered, can enable an attacker to execute arbitrary or remote code. In the case of CVE-2019-1208, an attacker successfully exploiting this vulnerability could gain the same rights as the current user in the system. If the current user has administrative privileges, the attacker can hijack the affected system — from installing or uninstalling programs and viewing and modifying data to creating user accounts with full privileges.

What is the potential impact of CVE-2019-1208?

A more tangible attack scenario would entail attackers sending socially engineered phishing emails to unknowing users and tricking them into accessing a malicious website (containing an exploit for CVE-2019-1208) via Internet Explorer. Alternatively, an attacker can send spam emails with attachments containing an exploit for the vulnerability. These attachments can be a Microsoft Office document that has the IE rendering engine enabled, or application files embedded with an ActiveX control that, in turn, contains an exploit for the vulnerability. Attackers could also compromise and host an exploit on legitimate websites, like those that accept content or input (i.e., advertisements) from users.

Figure 1. Code flow of VbsJoin

How was CVE-2019-1208 uncovered?

My research started with BinDiff, when I was trying to compare the changes made on the functions in vbscript.dll, a module that contains the API functions for the VBScript engine, between May and June. I saw that there were fixes made via the SafeArrayAddRef, SafeArrayReleaseData, and SafeArrayReleaseDescriptor functions.

Probing further, however, and inspired by a vulnerability (CVE-2018-8373) I previously uncovered, I used VBScriptClass and was able to trigger a UAF issue through these steps:

arr = Array(New MyClass) — Create a SafeArray and save the VBScriptclass: MyClass in arr[0]: Callback: arr = Array(0) — Join(arr) will trigger the MyClass ‘Public Default Property Get’ function callback. In this callback, create a new SafeArray to the variant arr. As shown in Figure 1, this new SafeArray is not protected by function SafeArrayAddRef. Thus, the normal code flow assumption is broken by this callback (as shown in Figure 1). arr(0) = Join(arr) — When back from the ‘Public Default Property Get’ callback, the code flow in VbsJoin will call SafeArrayReleaseData and SafeArrayReleaseDescriptor to decrease the reference count of SafeArrayData and SafeArrayDescriptor. However, the new SafeArray is not protected by SafeArrayAddRef, and the reference count of SafeArrayData and SafeArrayDescriptor is 0. Therefore, the new SafeArray’s SafeArrayData and SafeArrayDescriptor will be freed in the functions SafeArrayReleaseData and SafeArrayReleaseDescriptor, also shown in Figure 2.

Figure 2. Snapshots of code showing arr = Array(New MyClass) in memory (top), arr = Array(0) in memory, and the callback (highlighted, bottom)

When saving the VbsJoin return value to arr(0), the PoC crashes in vbscript!AccessArray (Figure 3) because the SafeArrayDescriptor is freed and the Variant arr still saves the pointer of the freed SafeArrayDescriptor.

Figure 3. Snapshot of code showing how the PoC crashed in vbscript!AccessArray

Did the PoC successfully trigger UAF?

In a way, yes, but to a limited extent. To demonstrate how UAF can be fully triggered, I used basic string/binary string (BSTR) as the data structure. SafeArray is a multidimensional array, but since VbsJoin can only process a one-dimensional array, I changed the SafeArray dimensions in the callback. Unfortunately, it still didn’t work. It throws a runtime error that says the array type does not match in Join. I used On Error Resume Next to bypass this runtime error. Figure 4 is the modified PoC.

Figure 4. Modified PoC that used On Error Resume Next

After getting 0x20 bytes of freed memory, I used BSTR whose size is 0x20 bytes to fake a big-size SafeArray. By using heap feng shui, this BSTR can reuse the 0x20 bytes freed memory stably. As shown in Figure 5 (top), I finally got a fake, one-dimensional SafeArray whose element number is 0x7ffffffff and element size is 1 byte:

Figure 5. Faked SafeArray (top) and fixed address for read/write (bottom)

I was able to make a fake SafeArray that can be used to read/write memory from 0x00000000 to 0x7fffffff. To leak some read/write address for exploitation, I applied Simon Zuckerbraun’s previous research and used heap spray to give me some fixed read/write address (0x28281000), as seen in Figure 4 (bottom).

How can this UAF vulnerability lead to remote code execution?

I used the Scripting.Dictionary object to perform remote code execution (RCE) as explained in Simon Zuckerbraun’s blog, but used another method to make a fake Dictionary. This time, I used BSTR and carried these out, as shown in Figure 6:

Use a read/write memory function to read the original Dictionary memory, save its data to one BSTR, and replace VBADictionary::Exists to kernel32!Winexec. Write the Winexec parameter (\..\calc.exe) to this BSTR. Save this BSTR to util_memory + 0x1000, and modify ‘util_memory + 0x1000 – 8 = 9’ to make fake_array(util_memory + 0x1000) to be an object. Use fake_array(util_memory + &h1000).Exists "dummy" to trigger the function Winexec.

Figure 6. Faked Dictionary memory layout Figure 7. Successfully carrying out RCE

What does this vulnerability mean for IE?

On August 13, 2019, VBScript, which has already been disabled in Windows 10, was disabled for Internet Explorer 11 in Windows 7, 8, and 8.1. Therefore, the PoC detailed here was developed in local mode. But as Microsoft says, this setting can still be enabled via Registry or Group Policy. All the same, users and organizations should always adopt best practices: Keep systems patched and updated, disable components if they are not needed (or restrict use), and foster cybersecurity awareness on vectors that may be used by attackers, such as spam emails and other socially engineered threats.

The complete details of the research are in this technical brief.