UMCI vs Internet Explorer: Exploring CVE-2017-8625

In the recent months, I have spent some time digging into Device Guard and how User Mode Code Integrity (UMCI) is implemented. If you aren’t familiar with Device Guard, you can read more about it here. Normally, UMCI prevents unapproved binaries from executing, restricts the Windows Scripting Host, and places PowerShell in Constrained Language mode. This makes obtaining code execution on a system fairly challenging. This post is going to highlight bypassing User Mode Code Integrity to instantiate objects that are normally blocked.

This bug was fixed in the August 2017 Patch Tuesday release under CVE-2017-8625. The patch fixed two different techniques as both of them abuse the fact that MSHTML isn’t enlightened. The other technique abused Microsoft Compiled HTML Help files (CHMs). This was blogged about by @Oddvarmoe and you can read more about that here: https://msitpros.com/?p=3909

As I mentioned above, the root cause of the bug was that MSHTML wasn’t enlightened in the context of UMCI. This essentially means that Device Guard’s UMCI component didn’t restrict MSHTML and simply let it execute everything with assumed “Full Trust.” Due to this, Internet Explorer permitted loading restricted objects. How, you might ask? The answer lies with Internet Explorer’s ability to interface with ActiveX and COM components =)

For example, the below HTML file will use a script tag to run some JScript. UMCI normally blocks instantiation of this object (and just about every other one). As a PoC, I just instantiated WScript.Shell and invoked the “Run” method to demonstrate I could instantiate a blocked object and invoke one of its methods.

As you can see below, “WScript.Shell” was blocked from instantiating via UMCI. Immediately following that, we instantiate “WScript.Shell” again via the HTML file and then start “calc.exe” via the “Run” method:

Since MSHTML didn’t conform to the same restrictions as other OS components, the WScript.Shell object was permitted to load and the run method was freely accessible.

It should be noted that Internet Explorer does apply zone security to prevent automatic execution of ActiveX controls via HTML. When opened, the user will see two prompts, one from IE asking permission to enable the ActiveX content and another security warning alerting the user that the content might be malicious. If an attacker is on a system but doesn’t have unsigned code execution (see WMImplant), all it takes is two User Hive registry changes to temporarily disable the ActiveX alerting, allowing for silent unsigned code-execution. Those two changes look like this:

Most UMCI bypasses are serviceable bugs per Microsoft, so be sure to send those over to secure@microsoft.com if you like CVEs =)

With CVE-2017-8625 issued, ActiveX/COM controls are no longer able to be loaded as Internet Explorer is now enlightened. This hopefully makes MSHTML bypasses a bit harder to find =)

Disclosure timeline:

12/13/2016: Report submitted to secure@microsoft.com
03/2017: Fix was pushed into RS1 (Creators Update)
08/08/2017: RS1 fix was back ported; CVE-2017-8625 issued

-Matt N.

WSH Injection: A Case Study

At BSides Nashville 2017, Casey Smith (@SubTee) and I gave a talk titled Windows Operating System Archaeology. At this talk, we released a handful of offensive techniques that utilized the Component Object Model (COM) in Windows. One such technique described was abusing attacker controlled input passed to calls to GetObject(), which I will be discussing here.

Some environments use whitelisting to prevent unsigned Windows Scripting Host (WSH) files from running, especially with the rise of malicious .js or .vbs files. However, by “injecting” our malicious code into a Microsoft signed WSH script, we can bypass such a restriction.

Before diving into the different scripts that can be used for injection, it’s important to understand some of the mechanics behind why this works. When abusing injection, we are taking advantage of attacker controlled input passed to GetObject() and then combining that with the “script:” or “scriptlet:” COM monikers.

GetObject()

This method allows you to access an already instantiated COM object. If there isn’t an instance of the object already (if invoked without a moniker), this call will fail. For example, accessing Microsoft Excel’s COM object via GetObject() would look like this:

Set obj = GetObject( , "Excel.Application")

For the above to work, an instance of Excel has to be running. You can read more about GetObject() here

COM Monikers

While GetObject() is interesting by itself, it only allows us to access an instance of an already instantiated COM object. To get around this, we can implement a COM moniker to facilitate our payload execution. If you aren’t familiar with COM monikers, you can read more about them here. There are various COM monikers on Windows that allow you to instantiate objects in various ways. From an offensive standpoint, you can use these monikers to execute malicious code. That is a topic for another blog post :-).

For this post, we will focus on the “script:” and “scriptlet:” monikers. These particular monikers interface with scrobj.dll and help facilitate execution of COM scriptlets, which will be the payload. This was discovered by Casey Smith (@SubTee) and discussed at DerbyCon 2016 as well as blogged about here.

An example COM scriptlet will look like this:

<?XML version="1.0"?>
var r = new ActiveXObject("WScript.Shell").Run("calc.exe");
]]>
</scriptlet>

You can also use James Forshaw’s (@tiraniddo) tool DotNetToJScript to extend the JScript/VBScript in the COM Scriptlet, allowing for Win32 API access and even Shellcode execution. When you combine one of these two monikers and various calls to GetObject(), a lot of fun is had.

Now that the very brief COM background is over, time to look at an example 🙂

PubPrn.vbs

On Windows 7+, there is a Microsoft Signed WSH script called “PubPrn.vbs,” which resides in “C:\Windows\System32\Printing_Admin_Scripts\en-US”. When looking at this particular script, it becomes apparent that it is taking input provided by the user (via command line arguments) and passing an argument to “GetObject()”.

This means that we can run this script and pass it the two arguments it expects. The first argument can be anything and the second argument is the payload via the script: moniker.

Note: If you provide a value that isn’t a network address for the first argument (since it expects a ServerName), you can add the “/b” switch to cscript.exe when calling to suppress any additional error messages.

Since VBScript relies on COM to perform actions, it is used heavily in numerous Microsoft signed scripts. While this is just one example, there are bound to be others that can be exploited in a similar fashion. I encourage you to go hunting 🙂

  • Matt N.